blob: 6840b2b5fe99dbab18653e36c0b12f53a6853d72 [file] [log] [blame]
Ben Cheng25b3c042013-11-20 14:45:36 -08001/* Return line number information of CU.
2 Copyright (C) 2004-2010 Red Hat, Inc.
3 This file is part of Red Hat elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2004.
5
6 Red Hat elfutils is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 2 of the License.
9
10 Red Hat elfutils is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with Red Hat elfutils; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
18
19 In addition, as a special exception, Red Hat, Inc. gives You the
20 additional right to link the code of Red Hat elfutils with code licensed
21 under any Open Source Initiative certified open source license
22 (http://www.opensource.org/licenses/index.php) which requires the
23 distribution of source code with any binary distribution and to
24 distribute linked combinations of the two. Non-GPL Code permitted under
25 this exception must only link to the code of Red Hat elfutils through
26 those well defined interfaces identified in the file named EXCEPTION
27 found in the source code files (the "Approved Interfaces"). The files
28 of Non-GPL Code may instantiate templates or use macros or inline
29 functions from the Approved Interfaces without causing the resulting
30 work to be covered by the GNU General Public License. Only Red Hat,
31 Inc. may make changes or additions to the list of Approved Interfaces.
32 Red Hat's grant of this exception is conditioned upon your not adding
33 any new exceptions. If you wish to add a new Approved Interface or
34 exception, please contact Red Hat. You must obey the GNU General Public
35 License in all respects for all of the Red Hat elfutils code and other
36 code used in conjunction with Red Hat elfutils except the Non-GPL Code
37 covered by this exception. If you modify this file, you may extend this
38 exception to your version of the file, but you are not obligated to do
39 so. If you do not wish to provide this exception without modification,
40 you must delete this exception statement from your version and license
41 this file solely under the GPL without exception.
42
43 Red Hat elfutils is an included package of the Open Invention Network.
44 An included package of the Open Invention Network is a package for which
45 Open Invention Network licensees cross-license their patents. No patent
46 license is granted, either expressly or impliedly, by designation as an
47 included package. Should you wish to participate in the Open Invention
48 Network licensing program, please visit www.openinventionnetwork.com
49 <http://www.openinventionnetwork.com>. */
50
51#ifdef HAVE_CONFIG_H
52# include <config.h>
53#endif
54
55#include <assert.h>
56#include <stdlib.h>
57#include <string.h>
58#include "dwarf.h"
59#include "libdwP.h"
60
61
62struct filelist
63{
64 Dwarf_Fileinfo info;
65 struct filelist *next;
66};
67
68struct linelist
69{
70 Dwarf_Line line;
71 struct linelist *next;
72};
73
74
75/* Compare by Dwarf_Line.addr, given pointers into an array of pointers. */
76static int
77compare_lines (const void *a, const void *b)
78{
79 Dwarf_Line *const *p1 = a;
80 Dwarf_Line *const *p2 = b;
81
82 if ((*p1)->addr == (*p2)->addr)
83 /* An end_sequence marker precedes a normal record at the same address. */
84 return (*p2)->end_sequence - (*p1)->end_sequence;
85
86 return (*p1)->addr - (*p2)->addr;
87}
88
89int
90dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
91{
92 if (unlikely (cudie == NULL
93 || INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit))
94 return -1;
95
96 int res = -1;
97
98 /* Get the information if it is not already known. */
99 struct Dwarf_CU *const cu = cudie->cu;
100 if (cu->lines == NULL)
101 {
102 /* Failsafe mode: no data found. */
103 cu->lines = (void *) -1l;
104 cu->files = (void *) -1l;
105
106 /* The die must have a statement list associated. */
107 Dwarf_Attribute stmt_list_mem;
108 Dwarf_Attribute *stmt_list = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list,
109 &stmt_list_mem);
110
111 /* Get the offset into the .debug_line section. NB: this call
112 also checks whether the previous dwarf_attr call failed. */
113 const unsigned char *lineendp;
114 const unsigned char *linep
115 = __libdw_formptr (stmt_list, IDX_debug_line, DWARF_E_NO_DEBUG_LINE,
116 (unsigned char **) &lineendp, NULL);
117 if (linep == NULL)
118 goto out;
119
120 /* Get the compilation directory. */
121 Dwarf_Attribute compdir_attr_mem;
122 Dwarf_Attribute *compdir_attr = INTUSE(dwarf_attr) (cudie,
123 DW_AT_comp_dir,
124 &compdir_attr_mem);
125 const char *comp_dir = INTUSE(dwarf_formstring) (compdir_attr);
126
127 if (unlikely (linep + 4 > lineendp))
128 {
129 invalid_data:
130 __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE);
131 goto out;
132 }
133
134 Dwarf *dbg = cu->dbg;
135 Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep);
136 unsigned int length = 4;
137 if (unlikely (unit_length == DWARF3_LENGTH_64_BIT))
138 {
139 if (unlikely (linep + 8 > lineendp))
140 goto invalid_data;
141 unit_length = read_8ubyte_unaligned_inc (dbg, linep);
142 length = 8;
143 }
144
145 /* Check whether we have enough room in the section. */
146 if (unit_length < 2 + length + 5 * 1
147 || unlikely (linep + unit_length > lineendp))
148 goto invalid_data;
149 lineendp = linep + unit_length;
150
151 /* The next element of the header is the version identifier. */
152 uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep);
153 if (unlikely (version < 2) || unlikely (version > 4))
154 {
155 __libdw_seterrno (DWARF_E_VERSION);
156 goto out;
157 }
158
159 /* Next comes the header length. */
160 Dwarf_Word header_length;
161 if (length == 4)
162 header_length = read_4ubyte_unaligned_inc (dbg, linep);
163 else
164 header_length = read_8ubyte_unaligned_inc (dbg, linep);
165 const unsigned char *header_start = linep;
166
167 /* Next the minimum instruction length. */
168 uint_fast8_t minimum_instr_len = *linep++;
169
170 /* Next the maximum operations per instruction, in version 4 format. */
171 uint_fast8_t max_ops_per_instr = 1;
172 if (version >= 4)
173 {
174 if (unlikely (lineendp - linep < 5))
175 goto invalid_data;
176 max_ops_per_instr = *linep++;
177 if (unlikely (max_ops_per_instr == 0))
178 goto invalid_data;
179 }
180
181 /* Then the flag determining the default value of the is_stmt
182 register. */
183 uint_fast8_t default_is_stmt = *linep++;
184
185 /* Now the line base. */
186 int_fast8_t line_base = (int8_t) *linep++;
187
188 /* And the line range. */
189 uint_fast8_t line_range = *linep++;
190
191 /* The opcode base. */
192 uint_fast8_t opcode_base = *linep++;
193
194 /* Remember array with the standard opcode length (-1 to account for
195 the opcode with value zero not being mentioned). */
196 const uint8_t *standard_opcode_lengths = linep - 1;
197 if (unlikely (lineendp - linep < opcode_base - 1))
198 goto invalid_data;
199 linep += opcode_base - 1;
200
201 /* First comes the list of directories. Add the compilation
202 directory first since the index zero is used for it. */
203 struct dirlist
204 {
205 const char *dir;
206 size_t len;
207 struct dirlist *next;
208 } comp_dir_elem =
209 {
210 .dir = comp_dir,
211 .len = comp_dir ? strlen (comp_dir) : 0,
212 .next = NULL
213 };
214 struct dirlist *dirlist = &comp_dir_elem;
215 unsigned int ndirlist = 1;
216
217 // XXX Directly construct array to conserve memory?
218 while (*linep != 0)
219 {
220 struct dirlist *new_dir =
221 (struct dirlist *) alloca (sizeof (*new_dir));
222
223 new_dir->dir = (char *) linep;
224 uint8_t *endp = memchr (linep, '\0', lineendp - linep);
225 if (endp == NULL)
226 goto invalid_data;
227 new_dir->len = endp - linep;
228 new_dir->next = dirlist;
229 dirlist = new_dir;
230 ++ndirlist;
231 linep = endp + 1;
232 }
233 /* Skip the final NUL byte. */
234 ++linep;
235
236 /* Rearrange the list in array form. */
237 struct dirlist **dirarray
238 = (struct dirlist **) alloca (ndirlist * sizeof (*dirarray));
239 for (unsigned int n = ndirlist; n-- > 0; dirlist = dirlist->next)
240 dirarray[n] = dirlist;
241
242 /* Now read the files. */
243 struct filelist null_file =
244 {
245 .info =
246 {
247 .name = "???",
248 .mtime = 0,
249 .length = 0
250 },
251 .next = NULL
252 };
253 struct filelist *filelist = &null_file;
254 unsigned int nfilelist = 1;
255
256 if (unlikely (linep >= lineendp))
257 goto invalid_data;
258 while (*linep != 0)
259 {
260 struct filelist *new_file =
261 (struct filelist *) alloca (sizeof (*new_file));
262
263 /* First comes the file name. */
264 char *fname = (char *) linep;
265 uint8_t *endp = memchr (fname, '\0', lineendp - linep);
266 if (endp == NULL)
267 goto invalid_data;
268 size_t fnamelen = endp - (uint8_t *) fname;
269 linep = endp + 1;
270
271 /* Then the index. */
272 Dwarf_Word diridx;
273 get_uleb128 (diridx, linep);
274 if (unlikely (diridx >= ndirlist))
275 {
276 __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
277 goto out;
278 }
279
280 if (*fname == '/')
281 /* It's an absolute path. */
282 new_file->info.name = fname;
283 else
284 {
285 new_file->info.name = libdw_alloc (dbg, char, 1,
286 dirarray[diridx]->len + 1
287 + fnamelen + 1);
288 char *cp = new_file->info.name;
289
290 if (dirarray[diridx]->dir != NULL)
291 {
292 /* This value could be NULL in case the DW_AT_comp_dir
293 was not present. We cannot do much in this case.
294 The easiest thing is to convert the path in an
295 absolute path. */
296 cp = stpcpy (cp, dirarray[diridx]->dir);
297 }
298 *cp++ = '/';
299 strcpy (cp, fname);
300 assert (strlen (new_file->info.name)
301 < dirarray[diridx]->len + 1 + fnamelen + 1);
302 }
303
304 /* Next comes the modification time. */
305 get_uleb128 (new_file->info.mtime, linep);
306
307 /* Finally the length of the file. */
308 get_uleb128 (new_file->info.length, linep);
309
310 new_file->next = filelist;
311 filelist = new_file;
312 ++nfilelist;
313 }
314 /* Skip the final NUL byte. */
315 ++linep;
316
317 /* Consistency check. */
318 if (unlikely (linep != header_start + header_length))
319 {
320 __libdw_seterrno (DWARF_E_INVALID_DWARF);
321 goto out;
322 }
323
324 /* We are about to process the statement program. Initialize the
325 state machine registers (see 6.2.2 in the v2.1 specification). */
326 Dwarf_Word addr = 0;
327 unsigned int op_index = 0;
328 unsigned int file = 1;
329 int line = 1;
330 unsigned int column = 0;
331 uint_fast8_t is_stmt = default_is_stmt;
332 bool basic_block = false;
333 bool prologue_end = false;
334 bool epilogue_begin = false;
335 unsigned int isa = 0;
336 unsigned int discriminator = 0;
337
338 /* Apply the "operation advance" from a special opcode
339 or DW_LNS_advance_pc (as per DWARF4 6.2.5.1). */
340 inline void advance_pc (unsigned int op_advance)
341 {
342 addr += minimum_instr_len * ((op_index + op_advance)
343 / max_ops_per_instr);
344 op_index = (op_index + op_advance) % max_ops_per_instr;
345 }
346
347 /* Process the instructions. */
348 struct linelist *linelist = NULL;
349 unsigned int nlinelist = 0;
350
351 /* Adds a new line to the matrix.
352 We cannot simply define a function because we want to use alloca. */
353#define NEW_LINE(end_seq) \
354 do { \
355 if (unlikely (add_new_line (alloca (sizeof (struct linelist)), \
356 end_seq))) \
357 goto invalid_data; \
358 } while (0)
359
360 inline bool add_new_line (struct linelist *new_line, bool end_sequence)
361 {
362 /* Set the line information. For some fields we use bitfields,
363 so we would lose information if the encoded values are too large.
364 Check just for paranoia, and call the data "invalid" if it
365 violates our assumptions on reasonable limits for the values. */
366#define SET(field) \
367 do { \
368 new_line->line.field = field; \
369 if (unlikely (new_line->line.field != field)) \
370 return true; \
371 } while (0)
372
373 SET (addr);
374 SET (op_index);
375 SET (file);
376 SET (line);
377 SET (column);
378 SET (is_stmt);
379 SET (basic_block);
380 SET (end_sequence);
381 SET (prologue_end);
382 SET (epilogue_begin);
383 SET (isa);
384 SET (discriminator);
385
386#undef SET
387
388 new_line->next = linelist;
389 linelist = new_line;
390 ++nlinelist;
391
392 return false;
393 }
394
395 while (linep < lineendp)
396 {
397 unsigned int opcode;
398 unsigned int u128;
399 int s128;
400
401 /* Read the opcode. */
402 opcode = *linep++;
403
404 /* Is this a special opcode? */
405 if (likely (opcode >= opcode_base))
406 {
407 /* Yes. Handling this is quite easy since the opcode value
408 is computed with
409
410 opcode = (desired line increment - line_base)
411 + (line_range * address advance) + opcode_base
412 */
413 int line_increment = (line_base
414 + (opcode - opcode_base) % line_range);
415
416 /* Perform the increments. */
417 line += line_increment;
418 advance_pc ((opcode - opcode_base) / line_range);
419
420 /* Add a new line with the current state machine values. */
421 NEW_LINE (0);
422
423 /* Reset the flags. */
424 basic_block = false;
425 prologue_end = false;
426 epilogue_begin = false;
427 discriminator = 0;
428 }
429 else if (opcode == 0)
430 {
431 /* This an extended opcode. */
432 if (unlikely (lineendp - linep < 2))
433 goto invalid_data;
434
435 /* The length. */
436 uint_fast8_t len = *linep++;
437
438 if (unlikely ((size_t) (lineendp - linep) < len))
439 goto invalid_data;
440
441 /* The sub-opcode. */
442 opcode = *linep++;
443
444 switch (opcode)
445 {
446 case DW_LNE_end_sequence:
447 /* Add a new line with the current state machine values.
448 The is the end of the sequence. */
449 NEW_LINE (1);
450
451 /* Reset the registers. */
452 addr = 0;
453 op_index = 0;
454 file = 1;
455 line = 1;
456 column = 0;
457 is_stmt = default_is_stmt;
458 basic_block = false;
459 prologue_end = false;
460 epilogue_begin = false;
461 isa = 0;
462 discriminator = 0;
463 break;
464
465 case DW_LNE_set_address:
466 /* The value is an address. The size is defined as
467 apporiate for the target machine. We use the
468 address size field from the CU header. */
469 op_index = 0;
470 if (unlikely (lineendp - linep < cu->address_size))
471 goto invalid_data;
472 if (__libdw_read_address_inc (dbg, IDX_debug_line, &linep,
473 cu->address_size, &addr))
474 goto out;
475 break;
476
477 case DW_LNE_define_file:
478 {
479 char *fname = (char *) linep;
480 uint8_t *endp = memchr (linep, '\0', lineendp - linep);
481 if (endp == NULL)
482 goto invalid_data;
483 size_t fnamelen = endp - linep;
484 linep = endp + 1;
485
486 unsigned int diridx;
487 get_uleb128 (diridx, linep);
488 Dwarf_Word mtime;
489 get_uleb128 (mtime, linep);
490 Dwarf_Word filelength;
491 get_uleb128 (filelength, linep);
492
493 struct filelist *new_file =
494 (struct filelist *) alloca (sizeof (*new_file));
495 if (fname[0] == '/')
496 new_file->info.name = fname;
497 else
498 {
499 new_file->info.name =
500 libdw_alloc (dbg, char, 1, (dirarray[diridx]->len + 1
501 + fnamelen + 1));
502 char *cp = new_file->info.name;
503
504 if (dirarray[diridx]->dir != NULL)
505 /* This value could be NULL in case the
506 DW_AT_comp_dir was not present. We
507 cannot do much in this case. The easiest
508 thing is to convert the path in an
509 absolute path. */
510 cp = stpcpy (cp, dirarray[diridx]->dir);
511 *cp++ = '/';
512 strcpy (cp, fname);
513 }
514
515 new_file->info.mtime = mtime;
516 new_file->info.length = filelength;
517 new_file->next = filelist;
518 filelist = new_file;
519 ++nfilelist;
520 }
521 break;
522
523 case DW_LNE_set_discriminator:
524 /* Takes one ULEB128 parameter, the discriminator. */
525 if (unlikely (standard_opcode_lengths[opcode] != 1))
526 goto invalid_data;
527
528 get_uleb128 (discriminator, linep);
529 break;
530
531 default:
532 /* Unknown, ignore it. */
533 if (unlikely ((size_t) (lineendp - (linep - 1)) < len))
534 goto invalid_data;
535 linep += len - 1;
536 break;
537 }
538 }
539 else if (opcode <= DW_LNS_set_isa)
540 {
541 /* This is a known standard opcode. */
542 switch (opcode)
543 {
544 case DW_LNS_copy:
545 /* Takes no argument. */
546 if (unlikely (standard_opcode_lengths[opcode] != 0))
547 goto invalid_data;
548
549 /* Add a new line with the current state machine values. */
550 NEW_LINE (0);
551
552 /* Reset the flags. */
553 basic_block = false;
554 prologue_end = false;
555 epilogue_begin = false;
556 discriminator = 0;
557 break;
558
559 case DW_LNS_advance_pc:
560 /* Takes one uleb128 parameter which is added to the
561 address. */
562 if (unlikely (standard_opcode_lengths[opcode] != 1))
563 goto invalid_data;
564
565 get_uleb128 (u128, linep);
566 advance_pc (u128);
567 break;
568
569 case DW_LNS_advance_line:
570 /* Takes one sleb128 parameter which is added to the
571 line. */
572 if (unlikely (standard_opcode_lengths[opcode] != 1))
573 goto invalid_data;
574
575 get_sleb128 (s128, linep);
576 line += s128;
577 break;
578
579 case DW_LNS_set_file:
580 /* Takes one uleb128 parameter which is stored in file. */
581 if (unlikely (standard_opcode_lengths[opcode] != 1))
582 goto invalid_data;
583
584 get_uleb128 (u128, linep);
585 file = u128;
586 break;
587
588 case DW_LNS_set_column:
589 /* Takes one uleb128 parameter which is stored in column. */
590 if (unlikely (standard_opcode_lengths[opcode] != 1))
591 goto invalid_data;
592
593 get_uleb128 (u128, linep);
594 column = u128;
595 break;
596
597 case DW_LNS_negate_stmt:
598 /* Takes no argument. */
599 if (unlikely (standard_opcode_lengths[opcode] != 0))
600 goto invalid_data;
601
602 is_stmt = 1 - is_stmt;
603 break;
604
605 case DW_LNS_set_basic_block:
606 /* Takes no argument. */
607 if (unlikely (standard_opcode_lengths[opcode] != 0))
608 goto invalid_data;
609
610 basic_block = true;
611 break;
612
613 case DW_LNS_const_add_pc:
614 /* Takes no argument. */
615 if (unlikely (standard_opcode_lengths[opcode] != 0))
616 goto invalid_data;
617
618 advance_pc ((255 - opcode_base) / line_range);
619 break;
620
621 case DW_LNS_fixed_advance_pc:
622 /* Takes one 16 bit parameter which is added to the
623 address. */
624 if (unlikely (standard_opcode_lengths[opcode] != 1)
625 || unlikely (lineendp - linep < 2))
626 goto invalid_data;
627
628 addr += read_2ubyte_unaligned_inc (dbg, linep);
629 op_index = 0;
630 break;
631
632 case DW_LNS_set_prologue_end:
633 /* Takes no argument. */
634 if (unlikely (standard_opcode_lengths[opcode] != 0))
635 goto invalid_data;
636
637 prologue_end = true;
638 break;
639
640 case DW_LNS_set_epilogue_begin:
641 /* Takes no argument. */
642 if (unlikely (standard_opcode_lengths[opcode] != 0))
643 goto invalid_data;
644
645 epilogue_begin = true;
646 break;
647
648 case DW_LNS_set_isa:
649 /* Takes one uleb128 parameter which is stored in isa. */
650 if (unlikely (standard_opcode_lengths[opcode] != 1))
651 goto invalid_data;
652
653 get_uleb128 (isa, linep);
654 break;
655 }
656 }
657 else
658 {
659 /* This is a new opcode the generator but not we know about.
660 Read the parameters associated with it but then discard
661 everything. Read all the parameters for this opcode. */
662 for (int n = standard_opcode_lengths[opcode]; n > 0; --n)
663 get_uleb128 (u128, linep);
664
665 /* Next round, ignore this opcode. */
666 continue;
667 }
668 }
669
670 /* Put all the files in an array. */
671 Dwarf_Files *files = libdw_alloc (dbg, Dwarf_Files,
672 sizeof (Dwarf_Files)
673 + nfilelist * sizeof (Dwarf_Fileinfo)
674 + (ndirlist + 1) * sizeof (char *),
675 1);
676 const char **dirs = (void *) &files->info[nfilelist];
677
678 files->nfiles = nfilelist;
679 while (nfilelist-- > 0)
680 {
681 files->info[nfilelist] = filelist->info;
682 filelist = filelist->next;
683 }
684 assert (filelist == NULL);
685
686 /* Put all the directory strings in an array. */
687 files->ndirs = ndirlist;
688 for (unsigned int i = 0; i < ndirlist; ++i)
689 dirs[i] = dirarray[i]->dir;
690 dirs[ndirlist] = NULL;
691
692 /* Remember the referring CU. */
693 files->cu = cu;
694
695 /* Make the file data structure available through the CU. */
696 cu->files = files;
697
698 void *buf = libdw_alloc (dbg, Dwarf_Lines, (sizeof (Dwarf_Lines)
699 + (sizeof (Dwarf_Line)
700 * nlinelist)), 1);
701
702 /* First use the buffer for the pointers, and sort the entries.
703 We'll write the pointers in the end of the buffer, and then
704 copy into the buffer from the beginning so the overlap works. */
705 assert (sizeof (Dwarf_Line) >= sizeof (Dwarf_Line *));
706 Dwarf_Line **sortlines = (buf + sizeof (Dwarf_Lines)
707 + ((sizeof (Dwarf_Line)
708 - sizeof (Dwarf_Line *)) * nlinelist));
709
710 /* The list is in LIFO order and usually they come in clumps with
711 ascending addresses. So fill from the back to probably start with
712 runs already in order before we sort. */
713 unsigned int i = nlinelist;
714 while (i-- > 0)
715 {
716 sortlines[i] = &linelist->line;
717 linelist = linelist->next;
718 }
719 assert (linelist == NULL);
720
721 /* Sort by ascending address. */
722 qsort (sortlines, nlinelist, sizeof sortlines[0], &compare_lines);
723
724 /* Now that they are sorted, put them in the final array.
725 The buffers overlap, so we've clobbered the early elements
726 of SORTLINES by the time we're reading the later ones. */
727 cu->lines = buf;
728 cu->lines->nlines = nlinelist;
729 for (i = 0; i < nlinelist; ++i)
730 {
731 cu->lines->info[i] = *sortlines[i];
732 cu->lines->info[i].files = files;
733 }
734
735 /* Success. */
736 res = 0;
737 }
738 else if (cu->lines != (void *) -1l)
739 /* We already have the information. */
740 res = 0;
741
742 if (likely (res == 0))
743 {
744 *lines = cu->lines;
745 *nlines = cu->lines->nlines;
746 }
747 out:
748
749 // XXX Eventually: unlocking here.
750
751 return res;
752}
753INTDEF(dwarf_getsrclines)