LCOV - code coverage report
Current view: top level - ddb - db_dwarf.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 185 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 14 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: db_dwarf.c,v 1.7 2017/10/27 08:40:15 mpi Exp $         */
       2             : /*
       3             :  * Copyright (c) 2014 Matthew Dempsky <matthew@dempsky.org>
       4             :  *
       5             :  * Permission to use, copy, modify, and distribute this software for any
       6             :  * purpose with or without fee is hereby granted, provided that the above
       7             :  * copyright notice and this permission notice appear in all copies.
       8             :  *
       9             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      10             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      11             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      12             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      13             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      14             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      15             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      16             :  */
      17             : 
      18             : #ifdef _KERNEL
      19             : #include <sys/param.h>
      20             : #include <sys/systm.h>
      21             : #include <machine/db_machdep.h>
      22             : #include <ddb/db_sym.h>
      23             : #ifdef DIAGNOSTIC
      24             : #define DWARN(fmt, ...) printf("ddb: " fmt "\n", __VA_ARGS__)
      25             : #else
      26             : #define DWARN(fmt, ...) ((void)0)
      27             : #endif
      28             : #else /* _KERNEL */
      29             : #include <err.h>
      30             : #include <stdbool.h>
      31             : #include <stdint.h>
      32             : #include <string.h>
      33             : #define DWARN warnx
      34             : #endif /* _KERNEL */
      35             : 
      36             : enum {
      37             :         DW_LNS_copy                     = 1,
      38             :         DW_LNS_advance_pc               = 2,
      39             :         DW_LNS_advance_line             = 3,
      40             :         DW_LNS_set_file                 = 4,
      41             :         DW_LNS_set_column               = 5,
      42             :         DW_LNS_negate_stmt              = 6,
      43             :         DW_LNS_set_basic_block          = 7,
      44             :         DW_LNS_const_add_pc             = 8,
      45             :         DW_LNS_fixed_advance_pc         = 9,
      46             :         DW_LNS_set_prologue_end         = 10,
      47             :         DW_LNS_set_epilogue_begin       = 11,
      48             : };
      49             : 
      50             : enum {
      51             :         DW_LNE_end_sequence             = 1,
      52             :         DW_LNE_set_address              = 2,
      53             :         DW_LNE_define_file              = 3,
      54             : };
      55             : 
      56             : struct dwbuf {
      57             :         const char *buf;
      58             :         size_t len;
      59             : };
      60             : 
      61             : static inline bool
      62           0 : read_bytes(struct dwbuf *d, void *v, size_t n)
      63             : {
      64           0 :         if (d->len < n)
      65           0 :                 return (false);
      66           0 :         memcpy(v, d->buf, n);
      67           0 :         d->buf += n;
      68           0 :         d->len -= n;
      69           0 :         return (true);
      70           0 : }
      71             : 
      72             : static bool
      73           0 : read_s8(struct dwbuf *d, int8_t *v)
      74             : {
      75           0 :         return (read_bytes(d, v, sizeof(*v)));
      76             : }
      77             : 
      78             : static bool
      79           0 : read_u8(struct dwbuf *d, uint8_t *v)
      80             : {
      81           0 :         return (read_bytes(d, v, sizeof(*v)));
      82             : }
      83             : 
      84             : static bool
      85           0 : read_u16(struct dwbuf *d, uint16_t *v)
      86             : {
      87           0 :         return (read_bytes(d, v, sizeof(*v)));
      88             : }
      89             : 
      90             : static bool
      91           0 : read_u32(struct dwbuf *d, uint32_t *v)
      92             : {
      93           0 :         return (read_bytes(d, v, sizeof(*v)));
      94             : }
      95             : 
      96             : static bool
      97           0 : read_u64(struct dwbuf *d, uint64_t *v)
      98             : {
      99           0 :         return (read_bytes(d, v, sizeof(*v)));
     100             : }
     101             : 
     102             : /* Read a DWARF LEB128 (little-endian base-128) value. */
     103             : static bool
     104           0 : read_leb128(struct dwbuf *d, uint64_t *v, bool signextend)
     105             : {
     106             :         unsigned int shift = 0;
     107             :         uint64_t res = 0;
     108           0 :         uint8_t x;
     109           0 :         while (shift < 64 && read_u8(d, &x)) {
     110           0 :                 res |= (uint64_t)(x & 0x7f) << shift;
     111           0 :                 shift += 7;
     112           0 :                 if ((x & 0x80) == 0) {
     113           0 :                         if (signextend && shift < 64 && (x & 0x40) != 0)
     114           0 :                                 res |= ~(uint64_t)0 << shift;
     115           0 :                         *v = res;
     116           0 :                         return (true);
     117             :                 }
     118             :         }
     119           0 :         return (false);
     120           0 : }
     121             : 
     122             : static bool
     123           0 : read_sleb128(struct dwbuf *d, int64_t *v)
     124             : {
     125           0 :         return (read_leb128(d, (uint64_t *)v, true));
     126             : }
     127             : 
     128             : static bool
     129           0 : read_uleb128(struct dwbuf *d, uint64_t *v)
     130             : {
     131           0 :         return (read_leb128(d, v, false));
     132             : }
     133             : 
     134             : /* Read a NUL terminated string. */
     135             : static bool
     136           0 : read_string(struct dwbuf *d, const char **s)
     137             : {
     138           0 :         const char *end = memchr(d->buf, '\0', d->len);
     139           0 :         if (end == NULL)
     140           0 :                 return (false);
     141           0 :         size_t n = end - d->buf + 1;
     142           0 :         *s = d->buf;
     143           0 :         d->buf += n;
     144           0 :         d->len -= n;
     145             :         return (true);
     146           0 : }
     147             : 
     148             : static bool
     149           0 : read_buf(struct dwbuf *d, struct dwbuf *v, size_t n)
     150             : {
     151           0 :         if (d->len < n)
     152           0 :                 return (false);
     153           0 :         v->buf = d->buf;
     154           0 :         v->len = n;
     155           0 :         d->buf += n;
     156           0 :         d->len -= n;
     157           0 :         return (true);
     158           0 : }
     159             : 
     160             : static bool
     161           0 : skip_bytes(struct dwbuf *d, size_t n)
     162             : {
     163           0 :         if (d->len < n)
     164           0 :                 return (false);
     165           0 :         d->buf += n;
     166           0 :         d->len -= n;
     167           0 :         return (true);
     168           0 : }
     169             : 
     170             : static bool
     171           0 : read_filename(struct dwbuf *names, const char **outdirname,
     172             :     const char **outbasename, uint8_t opcode_base, uint64_t file)
     173             : {
     174           0 :         if (file == 0)
     175           0 :                 return (false);
     176             : 
     177             :         /* Skip over opcode table. */
     178             :         size_t i;
     179           0 :         for (i = 1; i < opcode_base; i++) {
     180           0 :                 uint64_t dummy;
     181           0 :                 if (!read_uleb128(names, &dummy))
     182           0 :                         return (false);
     183           0 :         }
     184             : 
     185             :         /* Skip over directory name table for now. */
     186           0 :         struct dwbuf dirnames = *names;
     187           0 :         for (;;) {
     188           0 :                 const char *name;
     189           0 :                 if (!read_string(names, &name))
     190           0 :                         return (false);
     191           0 :                 if (*name == '\0')
     192           0 :                         break;
     193           0 :         }
     194             : 
     195             :         /* Locate file entry. */
     196           0 :         const char *basename = NULL;
     197           0 :         uint64_t dir = 0;
     198           0 :         for (i = 0; i < file; i++) {
     199           0 :                 uint64_t mtime, size;
     200           0 :                 if (!read_string(names, &basename) || *basename == '\0' ||
     201           0 :                     !read_uleb128(names, &dir) ||
     202           0 :                     !read_uleb128(names, &mtime) ||
     203           0 :                     !read_uleb128(names, &size))
     204           0 :                         return (false);
     205           0 :         }
     206             : 
     207           0 :         const char *dirname = NULL;
     208           0 :         for (i = 0; i < dir; i++) {
     209           0 :                 if (!read_string(&dirnames, &dirname) || *dirname == '\0')
     210           0 :                         return (false);
     211             :         }
     212             : 
     213           0 :         *outdirname = dirname;
     214           0 :         *outbasename = basename;
     215           0 :         return (true);
     216           0 : }
     217             : 
     218             : bool
     219           0 : db_dwarf_line_at_pc(const char *linetab, size_t linetabsize, uintptr_t pc,
     220             :     const char **outdirname, const char **outbasename, int *outline)
     221             : {
     222           0 :         struct dwbuf table = { .buf = linetab, .len = linetabsize };
     223             : 
     224             :         /*
     225             :          * For simplicity, we simply brute force search through the entire
     226             :          * line table each time.
     227             :          */
     228           0 :         uint32_t unitsize;
     229           0 :         struct dwbuf unit;
     230             : next:
     231             :         /* Line tables are a sequence of compilation unit entries. */
     232           0 :         if (!read_u32(&table, &unitsize) || unitsize >= 0xfffffff0 ||
     233           0 :             !read_buf(&table, &unit, unitsize))
     234           0 :                 return (false);
     235             : 
     236             :         uint16_t version;
     237             :         uint32_t header_size;
     238           0 :         if (!read_u16(&unit, &version) || version > 2 ||
     239           0 :             !read_u32(&unit, &header_size))
     240           0 :                 goto next;
     241             : 
     242           0 :         struct dwbuf headerstart = unit;
     243             :         uint8_t min_insn_length, default_is_stmt, line_range, opcode_base;
     244             :         int8_t line_base;
     245           0 :         if (!read_u8(&unit, &min_insn_length) ||
     246           0 :             !read_u8(&unit, &default_is_stmt) ||
     247           0 :             !read_s8(&unit, &line_base) ||
     248           0 :             !read_u8(&unit, &line_range) ||
     249           0 :             !read_u8(&unit, &opcode_base))
     250           0 :                 goto next;
     251             : 
     252             :         /*
     253             :          * Directory and file names are next in the header, but for now we
     254             :          * skip directly to the line number program.
     255             :          */
     256           0 :         struct dwbuf names = unit;
     257           0 :         unit = headerstart;
     258           0 :         if (!skip_bytes(&unit, header_size))
     259           0 :                 return (false);
     260             : 
     261             :         /* VM registers. */
     262           0 :         uint64_t address = 0, file = 1, line = 1, column = 0;
     263           0 :         uint8_t is_stmt = default_is_stmt;
     264             :         bool basic_block = false, end_sequence = false;
     265             :         bool prologue_end = false, epilogue_begin = false;
     266             : 
     267             :         /* Last line table entry emitted, if any. */
     268             :         bool have_last = false;
     269             :         uint64_t last_line = 0, last_file = 0;
     270             : 
     271             :         /* Time to run the line program. */
     272             :         uint8_t opcode;
     273           0 :         while (read_u8(&unit, &opcode)) {
     274             :                 bool emit = false, reset_basic_block = false;
     275             : 
     276           0 :                 if (opcode >= opcode_base) {
     277             :                         /* "Special" opcodes. */
     278           0 :                         uint8_t diff = opcode - opcode_base;
     279           0 :                         address += diff / line_range;
     280           0 :                         line += line_base + diff % line_range;
     281             :                         emit = true;
     282           0 :                 } else if (opcode == 0) {
     283             :                         /* "Extended" opcodes. */
     284           0 :                         uint64_t extsize;
     285           0 :                         struct dwbuf extra;
     286           0 :                         if (!read_uleb128(&unit, &extsize) ||
     287           0 :                             !read_buf(&unit, &extra, extsize) ||
     288           0 :                             !read_u8(&extra, &opcode))
     289           0 :                                 goto next;
     290           0 :                         switch (opcode) {
     291             :                         case DW_LNE_end_sequence:
     292             :                                 emit = true;
     293             :                                 end_sequence = true;
     294           0 :                                 break;
     295             :                         case DW_LNE_set_address:
     296           0 :                                 switch (extra.len) {
     297             :                                 case 4: {
     298           0 :                                         uint32_t address32;
     299           0 :                                         if (!read_u32(&extra, &address32))
     300           0 :                                                 goto next;
     301           0 :                                         address = address32;
     302           0 :                                         break;
     303           0 :                                 }
     304             :                                 case 8:
     305           0 :                                         if (!read_u64(&extra, &address))
     306           0 :                                                 goto next;
     307             :                                         break;
     308             :                                 default:
     309           0 :                                         DWARN("unexpected address length: %zu",
     310             :                                             extra.len);
     311           0 :                                         goto next;
     312             :                                 }
     313             :                                 break;
     314             :                         case DW_LNE_define_file:
     315             :                                 /* XXX: hope this isn't needed */
     316             :                         default:
     317           0 :                                 DWARN("unknown extended opcode: %d", opcode);
     318           0 :                                 goto next;
     319             :                         }
     320           0 :                 } else {
     321             :                         /* "Standard" opcodes. */
     322           0 :                         switch (opcode) {
     323             :                         case DW_LNS_copy:
     324             :                                 emit = true;
     325             :                                 reset_basic_block = true;
     326           0 :                                 break;
     327             :                         case DW_LNS_advance_pc: {
     328           0 :                                 uint64_t delta;
     329           0 :                                 if (!read_uleb128(&unit, &delta))
     330           0 :                                         goto next;
     331           0 :                                 address += delta * min_insn_length;
     332           0 :                                 break;
     333           0 :                         }
     334             :                         case DW_LNS_advance_line: {
     335           0 :                                 int64_t delta;
     336           0 :                                 if (!read_sleb128(&unit, &delta))
     337           0 :                                         goto next;
     338           0 :                                 line += delta;
     339           0 :                                 break;
     340           0 :                         }
     341             :                         case DW_LNS_set_file:
     342           0 :                                 if (!read_uleb128(&unit, &file))
     343           0 :                                         goto next;
     344             :                                 break;
     345             :                         case DW_LNS_set_column:
     346           0 :                                 if (!read_uleb128(&unit, &column))
     347           0 :                                         goto next;
     348             :                                 break;
     349             :                         case DW_LNS_negate_stmt:
     350           0 :                                 is_stmt = !is_stmt;
     351           0 :                                 break;
     352             :                         case DW_LNS_set_basic_block:
     353             :                                 basic_block = true;
     354           0 :                                 break;
     355             :                         case DW_LNS_const_add_pc:
     356           0 :                                 address += (255 - opcode_base) / line_range;
     357           0 :                                 break;
     358             :                         case DW_LNS_set_prologue_end:
     359             :                                 prologue_end = true;
     360           0 :                                 break;
     361             :                         case DW_LNS_set_epilogue_begin:
     362             :                                 epilogue_begin = true;
     363           0 :                                 break;
     364             :                         default:
     365           0 :                                 DWARN("unknown standard opcode: %d", opcode);
     366           0 :                                 goto next;
     367             :                         }
     368             :                 }
     369             : 
     370           0 :                 if (emit) {
     371           0 :                         if (address > pc) {
     372             :                                 /* Found an entry after our target PC. */
     373           0 :                                 if (!have_last) {
     374             :                                         /* Give up on this program. */
     375           0 :                                         break;
     376             :                                 }
     377             :                                 /* Return the last entry. */
     378           0 :                                 *outline = last_line;
     379           0 :                                 return (read_filename(&names, outdirname,
     380           0 :                                     outbasename, opcode_base, last_file));
     381             :                         }
     382             : 
     383           0 :                         last_file = file;
     384             :                         last_line = line;
     385             :                         have_last = true;
     386           0 :                 }
     387             : 
     388           0 :                 if (reset_basic_block)
     389           0 :                         basic_block = false;
     390           0 :         }
     391             : 
     392           0 :         goto next;
     393           0 : }
     394             : 
     395             : #ifndef _KERNEL
     396             : #include <sys/endian.h>
     397             : #include <sys/mman.h>
     398             : #include <sys/stat.h>
     399             : #include <elf.h>
     400             : #include <fcntl.h>
     401             : #include <stdio.h>
     402             : #include <stdlib.h>
     403             : #include <unistd.h>
     404             : 
     405             : #ifndef ELFDATA
     406             : #if BYTE_ORDER == LITTLE_ENDIAN
     407             : #define ELFDATA ELFDATA2LSB
     408             : #elif BYTE_ORDER == BIG_ENDIAN
     409             : #define ELFDATA ELFDATA2MSB
     410             : #else
     411             : #error Unsupported byte order
     412             : #endif
     413             : #endif /* !ELFDATA */
     414             : 
     415             : static void
     416             : usage(void)
     417             : {
     418             :         extern const char *__progname;
     419             :         errx(1, "usage: %s [-s] [-e filename] [addr addr ...]", __progname);
     420             : }
     421             : 
     422             : /*
     423             :  * Basic addr2line clone for stand-alone testing.
     424             :  */
     425             : int
     426             : main(int argc, char *argv[])
     427             : {
     428             :         const char *filename = "a.out";
     429             : 
     430             :         int ch;
     431             :         bool showdir = true;
     432             :         while ((ch = getopt(argc, argv, "e:s")) != EOF) {
     433             :                 switch (ch) {
     434             :                 case 'e':
     435             :                         filename = optarg;
     436             :                         break;
     437             :                 case 's':
     438             :                         showdir = false;
     439             :                         break;
     440             :                 default:
     441             :                         usage();
     442             :                 }
     443             :         }
     444             : 
     445             :         argc -= optind;
     446             :         argv += optind;
     447             : 
     448             :         /* Start by mapping the full file into memory. */
     449             :         int fd = open(filename, O_RDONLY);
     450             :         if (fd == -1)
     451             :                 err(1, "open");
     452             : 
     453             :         struct stat st;
     454             :         if (fstat(fd, &st) == -1)
     455             :                 err(1, "fstat");
     456             :         if (st.st_size < (off_t)sizeof(Elf_Ehdr))
     457             :                 errx(1, "file too small to be ELF");
     458             :         if ((uintmax_t)st.st_size > SIZE_MAX)
     459             :                 errx(1, "file too big to fit memory");
     460             :         size_t filesize = st.st_size;
     461             : 
     462             :         const char *p = mmap(NULL, filesize, PROT_READ, MAP_SHARED, fd, 0);
     463             :         if (p == MAP_FAILED)
     464             :                 err(1, "mmap");
     465             : 
     466             :         close(fd);
     467             : 
     468             :         /* Read and validate ELF header. */
     469             :         Elf_Ehdr ehdr;
     470             :         memcpy(&ehdr, p, sizeof(ehdr));
     471             :         if (!IS_ELF(ehdr))
     472             :                 errx(1, "file is not ELF");
     473             :         if (ehdr.e_ident[EI_CLASS] != ELFCLASS)
     474             :                 errx(1, "unexpected word size");
     475             :         if (ehdr.e_ident[EI_DATA] != ELFDATA)
     476             :                 errx(1, "unexpected data format");
     477             :         if (ehdr.e_shoff > filesize)
     478             :                 errx(1, "bogus section table offset");
     479             :         if (ehdr.e_shentsize < sizeof(Elf_Shdr))
     480             :                 errx(1, "unexpected section header size");
     481             :         if (ehdr.e_shnum > (filesize - ehdr.e_shoff) / ehdr.e_shentsize)
     482             :                 errx(1, "bogus section header count");
     483             :         if (ehdr.e_shstrndx >= ehdr.e_shnum)
     484             :                 errx(1, "bogus string table index");
     485             : 
     486             :         /* Find section header string table location and size. */
     487             :         Elf_Shdr shdr;
     488             :         memcpy(&shdr, p + ehdr.e_shoff + ehdr.e_shstrndx * ehdr.e_shentsize,
     489             :             sizeof(shdr));
     490             :         if (shdr.sh_type != SHT_STRTAB)
     491             :                 errx(1, "unexpected string table type");
     492             :         if (shdr.sh_offset > filesize)
     493             :                 errx(1, "bogus string table offset");
     494             :         if (shdr.sh_size > filesize - shdr.sh_offset)
     495             :                 errx(1, "bogus string table size");
     496             :         const char *shstrtab = p + shdr.sh_offset;
     497             :         size_t shstrtabsize = shdr.sh_size;
     498             : 
     499             :         /* Search through section table for .debug_line section. */
     500             :         size_t i;
     501             :         for (i = 0; i < ehdr.e_shnum; i++) {
     502             :                 memcpy(&shdr, p + ehdr.e_shoff + i * ehdr.e_shentsize,
     503             :                     sizeof(shdr));
     504             :                 if (0 == strncmp(".debug_line", shstrtab + shdr.sh_name,
     505             :                     shstrtabsize - shdr.sh_name))
     506             :                         break;
     507             :         }
     508             :         if (i == ehdr.e_shnum)
     509             :                 errx(1, "no DWARF line number table found");
     510             :         if (shdr.sh_offset > filesize)
     511             :                 errx(1, "bogus line table offset");
     512             :         if (shdr.sh_size > filesize - shdr.sh_offset)
     513             :                 errx(1, "bogus line table size");
     514             :         const char *linetab = p + shdr.sh_offset;
     515             :         size_t linetabsize = shdr.sh_size;
     516             : 
     517             :         const char *addrstr;
     518             :         while ((addrstr = *argv++) != NULL) {
     519             :                 unsigned long addr = strtoul(addrstr, NULL, 16);
     520             : 
     521             :                 const char *dir, *file;
     522             :                 int line;
     523             :                 if (!db_dwarf_line_at_pc(linetab, linetabsize, addr,
     524             :                     &dir, &file, &line)) {
     525             :                         dir = NULL;
     526             :                         file = "??";
     527             :                         line = 0;
     528             :                 }
     529             :                 if (showdir && dir != NULL)
     530             :                         printf("%s/", dir);
     531             :                 printf("%s:%d\n", file, line);
     532             :         }
     533             : 
     534             :         return (0);
     535             : }
     536             : #endif /* !_KERNEL */

Generated by: LCOV version 1.13