1 /* 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 (at) 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 62 struct filelist 63 { 64 Dwarf_Fileinfo info; 65 struct filelist *next; 66 }; 67 68 struct 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. */ 76 static int 77 compare_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 89 int 90 dwarf_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 } 753 INTDEF(dwarf_getsrclines) 754