From patchwork Wed Apr 10 03:45:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Merey X-Patchwork-Id: 88269 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id CD1333858CDB for ; Wed, 10 Apr 2024 03:46:07 +0000 (GMT) X-Original-To: elfutils-devel@sourceware.org Delivered-To: elfutils-devel@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 9C4DA3858D39 for ; Wed, 10 Apr 2024 03:45:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9C4DA3858D39 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 9C4DA3858D39 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1712720755; cv=none; b=YwWDCP3GM/vmjxPPWES+UixBzOI6BafxtrNLuqR3krWbfbXEXqNG12rlHPPt1+4UavKGKi7uSDx0bMwiIegwMBAngX663j9Y4M5R0rSTzXUaMLhcxJlWUGab/3wzCbXzPpa2PCDzTWY6Q2lEuwktvfx5cV9VoWxAMphtk/SUg4w= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1712720755; c=relaxed/simple; bh=8f9BJUEmLGQJlaZ48g5sG7TS5sks5MNjWJQw8fT9nWs=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=TdlnBpfOHHgfQq5TvO/e++utyi6gY1yuOnLW6eZlisx5ewGdnZxx++3yNBQWYmzdWiTug2rYs79cwBsN5jkMxNngjCHSkPBCm/cLii1psfPqXSZ/HStrYaaKldk7V8zy45Lk9NQQ6NiHkNw4hnMWrqzlmWbb/wQ2PGVcPbPTRVw= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1712720749; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=OUBMDCeZtZVFbqb5fdi78z1gSv1j9xtGbzRgvvNbotU=; b=TtHpoQoghBbRMMkWJM1KRFdtCO61oDRg3zhWVPm8JYDxDtsahE1ugvT17KHQhPVIFxKXJ4 KxtQVjnIdo4BU8PY2CYvGT4OsehXI9mV4QMPNTQHZDH1kMAVXAphP/phXdT1b9MMFc9Q3+ cqj3KN30LAX95w/W1gbSGfYZAsW6bos= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-600-Rnl12ZdHOuSG5sQrt_Ielw-1; Tue, 09 Apr 2024 23:45:44 -0400 X-MC-Unique: Rnl12ZdHOuSG5sQrt_Ielw-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id DC72029AA2C2 for ; Wed, 10 Apr 2024 03:45:43 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.22.8.94]) by smtp.corp.redhat.com (Postfix) with ESMTP id 270E8444462; Wed, 10 Apr 2024 03:45:43 +0000 (UTC) From: Aaron Merey To: elfutils-devel@sourceware.org Cc: Aaron Merey Subject: [PATCH] libdw: dwarf_getsrcfiles should not imply dwarf_getsrclines Date: Tue, 9 Apr 2024 23:45:39 -0400 Message-ID: <20240410034539.164402-1-amerey@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: elfutils-devel@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Elfutils-devel mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: elfutils-devel-bounces+patchwork=sourceware.org@sourceware.org dwarf_getsrcfiles causes line data to be read in addition to file data. This is wasteful for programs which only need file or directory names. Debuginfod server is one such example. Fix this by moving the srcfile reading in read_srclines into a separate function read_srcfiles. This change improves debuginfod server's max resident set size by up to 75% during rpm indexing. * libdw/dwarf_getsrcfiles.c (dwarf_getsrcfiles): Replace dwarf_getsrclines and __libdw_getsrclines with __libdw_getsrcfiles. * libdw/dwarf_getsrclines.c (read_line_header): New function. (read_srcfiles): New function. (read_srclines): Move srcfile reading into read_srcfiles. Add parameter to use cached srcfiles if available. Also merge srcfiles with any files from DW_LNE_define_file. (__libdw_getsrclines): Changed to call get_lines_or_files. (__libdw_getsrcfiles): New function. Calls get_lines_or_files. (get_lines_or_files): New function based on the old __libdw_getsrclines. Call read_srcfiles if linesp is NULL, otherwise call read_srclines. Pass previously read srcfiles to read_srclines if available. * libdw/dwarf_macro_getsrcfiles.c (dwarf_macro_getsrcfiles): Replace __libdw_getsrclines with __libdw_getsrcfiles. * libdw/libdwP.h (__libdw_getsrcfiles): New declaration. * tests/.gitignore: Add new test binary. * tests/get-files.c: Verify that dwarf_getsrcfiles does not cause srclines to be read. * tests/get-lines.c: Verify that srclines can be read after srcfiles have been read. * tests/Makefile.am: Add new testfiles. * tests/get-files-define-file.c: Print file names before and after reading DW_LNE_define_file. * tests/run-get-files.sh: Add get-files-define-file test. * tests/testfile-define-file.bz2: New testfile. Copy of testfile36.debug but with a line program consisting of two DW_LNE_define_file opcodes. https://sourceware.org/bugzilla/show_bug.cgi?id=27405 Signed-off-by: Aaron Merey --- v2 changes: Restored support for DW_LNE_define_file. Added DW_LNE_define_file testcase and testfile. Added new function get_lines_or_files which is based on the old __libdw_getsrclines. __libdw_getsrclines and __libdw_getsrcfiles now call get_lines_or_files. Mentioned max resident set size improvement in the commit message. I did not mention a 5% speed up to rpm indexing that I have previously talked about since I could not reliably reproduce it with v2 of the patch. I suspect that speed up may have been due to the smaller max RSS reducing swapping during that round of testing. libdw/dwarf_getsrcfiles.c | 24 +- libdw/dwarf_getsrclines.c | 661 ++++++++++++++++++++++---------- libdw/dwarf_macro_getsrcfiles.c | 4 +- libdw/libdwP.h | 10 + tests/.gitignore | 1 + tests/Makefile.am | 6 +- tests/get-files-define-file.c | 162 ++++++++ tests/get-files.c | 8 + tests/get-lines.c | 20 +- tests/run-get-files.sh | 65 +++- tests/testfile-define-file.bz2 | Bin 0 -> 24910 bytes 11 files changed, 734 insertions(+), 227 deletions(-) create mode 100644 tests/get-files-define-file.c create mode 100644 tests/testfile-define-file.bz2 diff --git a/libdw/dwarf_getsrcfiles.c b/libdw/dwarf_getsrcfiles.c index cd2e5b5a..24e4b7d2 100644 --- a/libdw/dwarf_getsrcfiles.c +++ b/libdw/dwarf_getsrcfiles.c @@ -70,10 +70,9 @@ dwarf_getsrcfiles (Dwarf_Die *cudie, Dwarf_Files **files, size_t *nfiles) { /* We are only interested in the files, the lines will always come from the skeleton. */ - res = __libdw_getsrclines (cu->dbg, dwp_off, + res = __libdw_getsrcfiles (cu->dbg, dwp_off, __libdw_getcompdir (cudie), - cu->address_size, NULL, - &cu->files); + cu->address_size, &cu->files); } } else @@ -89,12 +88,19 @@ dwarf_getsrcfiles (Dwarf_Die *cudie, Dwarf_Files **files, size_t *nfiles) } else { - Dwarf_Lines *lines; - size_t nlines; - - /* Let the more generic function do the work. It'll create more - data but that will be needed in an real program anyway. */ - res = INTUSE(dwarf_getsrclines) (cudie, &lines, &nlines); + /* The die must have a statement list associated. */ + Dwarf_Attribute stmt_list_mem; + Dwarf_Attribute *stmt_list = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list, + &stmt_list_mem); + + Dwarf_Off debug_line_offset; + if (__libdw_formptr (stmt_list, IDX_debug_line, DWARF_E_NO_DEBUG_LINE, + NULL, &debug_line_offset) == NULL) + return -1; + + res = __libdw_getsrcfiles (cu->dbg, debug_line_offset, + __libdw_getcompdir (cudie), + cu->address_size, &cu->files); } } else if (cu->files != (void *) -1l) diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c index 69e10c7b..e30695f3 100644 --- a/libdw/dwarf_getsrclines.c +++ b/libdw/dwarf_getsrclines.c @@ -52,6 +52,11 @@ struct linelist size_t sequence; }; +struct dirlist +{ + const char *dir; + size_t len; +}; /* Compare by Dwarf_Line.addr, given pointers into an array of pointers. */ static int @@ -77,6 +82,28 @@ compare_lines (const void *a, const void *b) : 0; } +/* Decoded .debug_line program header. */ +struct line_header +{ + /* Header entries */ + Dwarf_Word unit_length; + unsigned int length; + uint_fast16_t version; + size_t line_address_size; + size_t segment_selector_size; + Dwarf_Word header_length; + const unsigned char *header_start; + uint_fast8_t minimum_instr_len; + uint_fast8_t max_ops_per_instr; + uint_fast8_t default_is_stmt; + int_fast8_t line_base; + uint_fast8_t line_range; + uint_fast8_t opcode_base; + const uint8_t *standard_opcode_lengths; + unsigned int debug_str_offset; /* CUBIN only */ + size_t files_start; +}; + struct line_state { Dwarf_Word addr; @@ -155,127 +182,81 @@ add_new_line (struct line_state *state, struct linelist *new_line) return false; } +/* Read the .debug_line program header. Return 0 if sucessful, otherwise set + libdw errno and return -1. */ + static int -read_srclines (Dwarf *dbg, - const unsigned char *linep, const unsigned char *lineendp, - const char *comp_dir, unsigned address_size, - Dwarf_Lines **linesp, Dwarf_Files **filesp) +read_line_header (Dwarf *dbg, unsigned address_size, + const unsigned char *linep, const unsigned char *lineendp, + struct line_header *lh) { - int res = -1; - - struct filelist *filelist = NULL; - size_t nfilelist = 0; - size_t ndirlist = 0; - - /* If there are a large number of lines, files or dirs don't blow up - the stack. Stack allocate some entries, only dynamically malloc - when more than MAX. */ -#define MAX_STACK_ALLOC 4096 -#define MAX_STACK_LINES (MAX_STACK_ALLOC / 2) -#define MAX_STACK_FILES (MAX_STACK_ALLOC / 4) -#define MAX_STACK_DIRS (MAX_STACK_ALLOC / 16) - - /* Initial statement program state (except for stmt_list, see below). */ - struct line_state state = - { - .linelist = NULL, - .nlinelist = 0, - .addr = 0, - .op_index = 0, - .file = 1, - /* We only store int but want to check for overflow (see SET above). */ - .line = 1, - .column = 0, - .basic_block = false, - .prologue_end = false, - .epilogue_begin = false, - .isa = 0, - .discriminator = 0, - .context = 0, - .function_name = 0 - }; - - /* The dirs normally go on the stack, but if there are too many - we alloc them all. Set up stack storage early, so we can check on - error if we need to free them or not. */ - struct dirlist - { - const char *dir; - size_t len; - }; - struct dirlist dirstack[MAX_STACK_DIRS]; - struct dirlist *dirarray = dirstack; + const unsigned char *line_start = linep; if (unlikely (linep + 4 > lineendp)) - { - invalid_data: - __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); - goto out; - } + goto invalid_data; - Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep); - unsigned int length = 4; - if (unlikely (unit_length == DWARF3_LENGTH_64_BIT)) + lh->unit_length = read_4ubyte_unaligned_inc (dbg, linep); + lh->length = 4; + if (unlikely (lh->unit_length == DWARF3_LENGTH_64_BIT)) { if (unlikely (linep + 8 > lineendp)) goto invalid_data; - unit_length = read_8ubyte_unaligned_inc (dbg, linep); - length = 8; + lh->unit_length = read_8ubyte_unaligned_inc (dbg, linep); + lh->length = 8; } /* Check whether we have enough room in the section. */ - if (unlikely (unit_length > (size_t) (lineendp - linep))) + if (unlikely (lh->unit_length > (size_t) (lineendp - linep))) goto invalid_data; - lineendp = linep + unit_length; + lineendp = linep + lh->unit_length; /* The next element of the header is the version identifier. */ if ((size_t) (lineendp - linep) < 2) goto invalid_data; - uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep); - if (unlikely (version < 2) || unlikely (version > 5)) + lh->version = read_2ubyte_unaligned_inc (dbg, linep); + if (unlikely (lh->version < 2) || unlikely (lh->version > 5)) { __libdw_seterrno (DWARF_E_VERSION); - goto out; + return -1; } /* DWARF5 explicitly lists address and segment_selector sizes. */ - if (version >= 5) + if (lh->version >= 5) { if ((size_t) (lineendp - linep) < 2) goto invalid_data; - size_t line_address_size = *linep++; - size_t segment_selector_size = *linep++; - if (line_address_size != address_size || segment_selector_size != 0) + lh->line_address_size = *linep++; + lh->segment_selector_size = *linep++; + if (lh->line_address_size != address_size || lh->segment_selector_size != 0) goto invalid_data; } /* Next comes the header length. */ - Dwarf_Word header_length; - if (length == 4) + if (lh->length == 4) { if ((size_t) (lineendp - linep) < 4) goto invalid_data; - header_length = read_4ubyte_unaligned_inc (dbg, linep); + lh->header_length = read_4ubyte_unaligned_inc (dbg, linep); } else { if ((size_t) (lineendp - linep) < 8) goto invalid_data; - header_length = read_8ubyte_unaligned_inc (dbg, linep); + lh->header_length = read_8ubyte_unaligned_inc (dbg, linep); } - const unsigned char *header_start = linep; + lh->header_start = linep; /* Next the minimum instruction length. */ - uint_fast8_t minimum_instr_len = *linep++; + lh->minimum_instr_len = *linep++; /* Next the maximum operations per instruction, in version 4 format. */ - uint_fast8_t max_ops_per_instr = 1; - if (version >= 4) + lh->max_ops_per_instr = 1; + if (lh->version >= 4) { if (unlikely ((size_t) (lineendp - linep) < 1)) goto invalid_data; - max_ops_per_instr = *linep++; - if (unlikely (max_ops_per_instr == 0)) + lh->max_ops_per_instr = *linep++; + if (unlikely (lh->max_ops_per_instr == 0)) goto invalid_data; } @@ -285,23 +266,71 @@ read_srclines (Dwarf *dbg, /* Then the flag determining the default value of the is_stmt register. */ - uint_fast8_t default_is_stmt = *linep++; + lh->default_is_stmt = *linep++; /* Now the line base. */ - int_fast8_t line_base = (int8_t) *linep++; + lh->line_base = (int8_t) *linep++; /* And the line range. */ - uint_fast8_t line_range = *linep++; + lh->line_range = *linep++; /* The opcode base. */ - uint_fast8_t opcode_base = *linep++; + lh->opcode_base = *linep++; /* Remember array with the standard opcode length (-1 to account for the opcode with value zero not being mentioned). */ - const uint8_t *standard_opcode_lengths = linep - 1; - if (unlikely (lineendp - linep < opcode_base - 1)) + lh->standard_opcode_lengths = linep - 1; + if (unlikely (lineendp - linep < lh->opcode_base - 1)) goto invalid_data; - linep += opcode_base - 1; + linep += lh->opcode_base - 1; + + /* Record beginning of the file information. */ + lh->files_start = (size_t) (linep - line_start); + + return 0; + +invalid_data: + __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); + return -1; +} + +/* If there are a large number of lines, files or dirs don't blow up + the stack. Stack allocate some entries, only dynamically malloc + when more than MAX. */ +#define MAX_STACK_ALLOC 4096 +#define MAX_STACK_LINES (MAX_STACK_ALLOC / 2) +#define MAX_STACK_FILES (MAX_STACK_ALLOC / 4) +#define MAX_STACK_DIRS (MAX_STACK_ALLOC / 16) + +static int +read_srcfiles (Dwarf *dbg, + const unsigned char *linep, const unsigned char *lineendp, + const char *comp_dir, unsigned address_size, + struct line_header *lh, Dwarf_Files **filesp) +{ + if (filesp == NULL) + return -1; + + struct line_header lh_local; + + if (lh == NULL) + { + if (read_line_header (dbg, address_size, linep, lineendp, &lh_local) != 0) + return -1; + lh = &lh_local; + } + + int res = -1; + + struct filelist *filelist = NULL; + size_t nfilelist = 0; + size_t ndirlist = 0; + + /* The dirs normally go on the stack, but if there are too many + we alloc them all. Set up stack storage early, so we can check on + error if we need to free them or not. */ + struct dirlist dirstack[MAX_STACK_DIRS]; + struct dirlist *dirarray = dirstack; /* To read DWARF5 dir and file lists we need to know the forms. For now we skip everything, except the DW_LNCT_path and @@ -311,12 +340,18 @@ read_srclines (Dwarf *dbg, unsigned char form_path = -1; /* Which forms is DW_LNCT_path. */ unsigned char form_idx = -1; /* And which is DW_LNCT_directory_index. */ + /* Set lineendp to the end of the file information. */ + lineendp = lh->header_start + lh->header_length; + + /* Advance linep to the beginning of the header's srcfile information. */ + linep += lh->files_start; + /* To read/skip form data. */ Dwarf_CU fake_cu = { .dbg = dbg, .sec_idx = IDX_debug_line, .version = 5, - .offset_size = length, + .offset_size = lh->length, .address_size = address_size, .startp = (void *) linep, .endp = (void *) lineendp, @@ -324,7 +359,7 @@ read_srclines (Dwarf *dbg, /* First count the entries. */ size_t ndirs = 0; - if (version < 5) + if (lh->version < 5) { const unsigned char *dirp = linep; while (dirp < lineendp && *dirp != 0) @@ -395,7 +430,7 @@ read_srclines (Dwarf *dbg, /* Entry zero is implicit for older versions, but explicit for 5+. */ struct dirlist comp_dir_elem; - if (version < 5) + if (lh->version < 5) { /* First comes the list of directories. Add the compilation directory first since the index zero is used for it. */ @@ -482,7 +517,7 @@ read_srclines (Dwarf *dbg, fl; }) /* Now read the files. */ - if (version < 5) + if (lh->version < 5) { if (unlikely (linep >= lineendp)) goto invalid_data; @@ -662,29 +697,114 @@ read_srclines (Dwarf *dbg, } } - unsigned int debug_str_offset = 0; - if (unlikely (linep == header_start + header_length - 4)) + if (unlikely (linep == lh->header_start + lh->header_length - 4)) { /* CUBINs contain an unsigned 4-byte offset */ - debug_str_offset = read_4ubyte_unaligned_inc (dbg, linep); + lh->debug_str_offset = read_4ubyte_unaligned_inc (dbg, linep); } /* Consistency check. */ - if (unlikely (linep != header_start + header_length)) + if (unlikely (linep != lh->header_start + lh->header_length)) + goto invalid_data; + + /* Put all the files in an array. */ + Dwarf_Files *files = libdw_alloc (dbg, Dwarf_Files, + sizeof (Dwarf_Files) + + nfilelist * sizeof (Dwarf_Fileinfo) + + (ndirlist + 1) * sizeof (char *), + 1); + + if (unlikely (files == NULL)) + goto no_mem; + + const char **dirs = (void *) &files->info[nfilelist]; + + struct filelist *fileslist = filelist; + files->nfiles = nfilelist; + for (size_t n = nfilelist; n > 0; n--) { - __libdw_seterrno (DWARF_E_INVALID_DWARF); - goto out; + files->info[n - 1] = fileslist->info; + fileslist = fileslist->next; } + assert (fileslist == NULL); + + /* Put all the directory strings in an array. */ + files->ndirs = ndirlist; + for (unsigned int i = 0; i < ndirlist; ++i) + dirs[i] = dirarray[i].dir; + dirs[ndirlist] = NULL; + + /* Pass the file data structure to the caller. */ + *filesp = files; + + res = 0; + goto out; + +invalid_data: + __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); + +out: + if (dirarray != dirstack) + free (dirarray); + for (size_t i = MAX_STACK_FILES; i < nfilelist; i++) + { + struct filelist *fl = filelist->next; + free (filelist); + filelist = fl; + } + + return res; +} + +static int +read_srclines (Dwarf *dbg, + const unsigned char *linep, const unsigned char *lineendp, + const char *comp_dir, unsigned address_size, + Dwarf_Lines **linesp, Dwarf_Files **filesp, + bool use_cached_files) +{ + int res = -1; + struct line_header lh; + + if (read_line_header (dbg, address_size, linep, lineendp, &lh) != 0) + return res; + + /* Use the filesp srcfiles if they've already been read. */ + if (!use_cached_files + && read_srcfiles (dbg, linep, lineendp, comp_dir, + address_size, &lh, filesp) != 0) + return res; + + /* Initial statement program state (except for stmt_list, see below). */ + struct line_state state = + { + .linelist = NULL, + .nlinelist = 0, + .addr = 0, + .op_index = 0, + .file = 1, + /* We only store int but want to check for overflow (see SET above). */ + .line = 1, + .column = 0, + .basic_block = false, + .prologue_end = false, + .epilogue_begin = false, + .isa = 0, + .discriminator = 0, + .context = 0, + .function_name = 0 + }; /* We are about to process the statement program. Most state machine registers have already been initialize above. Just add the is_stmt default. See 6.2.2 in the v2.1 specification. */ - state.is_stmt = default_is_stmt; + state.is_stmt = lh.default_is_stmt; /* Apply the "operation advance" from a special opcode or DW_LNS_advance_pc (as per DWARF4 6.2.5.1). */ #define advance_pc(op_advance) \ - run_advance_pc (&state, op_advance, minimum_instr_len, max_ops_per_instr) + run_advance_pc (&state, op_advance, lh.minimum_instr_len, \ + lh.max_ops_per_instr) /* Process the instructions. */ @@ -697,12 +817,26 @@ read_srclines (Dwarf *dbg, ? &llstack[state.nlinelist] \ : malloc (sizeof (struct linelist))); \ if (unlikely (ll == NULL)) \ - goto no_mem; \ + { \ + __libdw_seterrno (DWARF_E_NOMEM); \ + goto out; \ + } \ state.end_sequence = end_seq; \ if (unlikely (add_new_line (&state, ll))) \ goto invalid_data; \ } while (0) + /* If DW_LNE_define_file is present, then additional files will be + added to filesp. */ + size_t nfilelist = 0; + struct filelist *filelist = NULL; + + /* Set lineendp to the end of the line program. */ + lineendp = linep + lh.length + lh.unit_length; + + /* Set linep to the beginning of the line program. */ + linep = lh.header_start + lh.header_length; + while (linep < lineendp) { unsigned int opcode; @@ -713,9 +847,9 @@ read_srclines (Dwarf *dbg, opcode = *linep++; /* Is this a special opcode? */ - if (likely (opcode >= opcode_base)) + if (likely (opcode >= lh.opcode_base)) { - if (unlikely (line_range == 0)) + if (unlikely (lh.line_range == 0)) goto invalid_data; /* Yes. Handling this is quite easy since the opcode value @@ -724,12 +858,12 @@ read_srclines (Dwarf *dbg, opcode = (desired line increment - line_base) + (line_range * address advance) + opcode_base */ - int line_increment = (line_base - + (opcode - opcode_base) % line_range); + int line_increment = (lh.line_base + + (opcode - lh.opcode_base) % lh.line_range); /* Perform the increments. */ state.line += line_increment; - advance_pc ((opcode - opcode_base) / line_range); + advance_pc ((opcode - lh.opcode_base) / lh.line_range); /* Add a new line with the current state machine values. */ NEW_LINE (0); @@ -768,7 +902,7 @@ read_srclines (Dwarf *dbg, state.file = 1; state.line = 1; state.column = 0; - state.is_stmt = default_is_stmt; + state.is_stmt = lh.default_is_stmt; state.basic_block = false; state.prologue_end = false; state.epilogue_begin = false; @@ -790,63 +924,85 @@ read_srclines (Dwarf *dbg, goto out; break; - case DW_LNE_define_file: - { - char *fname = (char *) linep; - uint8_t *endp = memchr (linep, '\0', lineendp - linep); - if (endp == NULL) - goto invalid_data; - size_t fnamelen = endp - linep; - linep = endp + 1; - - unsigned int diridx; - if (unlikely (linep >= lineendp)) - goto invalid_data; - get_uleb128 (diridx, linep, lineendp); - if (unlikely (diridx >= ndirlist)) + case DW_LNE_define_file: + { + char *fname = (char *) linep; + uint8_t *endp = memchr (linep, '\0', lineendp - linep); + if (endp == NULL) + goto invalid_data; + size_t fnamelen = endp - linep; + linep = endp + 1; + + unsigned int diridx; + if (unlikely (linep >= lineendp)) + goto invalid_data; + get_uleb128 (diridx, linep, lineendp); + + size_t ndirs = (*filesp)->ndirs; + if (unlikely (diridx >= ndirs)) + { + __libdw_seterrno (DWARF_E_INVALID_DIR_IDX); + goto invalid_data; + } + Dwarf_Word mtime; + if (unlikely (linep >= lineendp)) + goto invalid_data; + get_uleb128 (mtime, linep, lineendp); + Dwarf_Word filelength; + if (unlikely (linep >= lineendp)) + goto invalid_data; + get_uleb128 (filelength, linep, lineendp); + + /* Add new_file to filelist that will be merged with filesp. */ + struct filelist *new_file = malloc (sizeof (struct filelist)); + if (unlikely (new_file == NULL)) { - __libdw_seterrno (DWARF_E_INVALID_DIR_IDX); - goto invalid_data; + __libdw_seterrno (DWARF_E_NOMEM); + goto out; } - Dwarf_Word mtime; - if (unlikely (linep >= lineendp)) - goto invalid_data; - get_uleb128 (mtime, linep, lineendp); - Dwarf_Word filelength; - if (unlikely (linep >= lineendp)) - goto invalid_data; - get_uleb128 (filelength, linep, lineendp); - - struct filelist *new_file = NEW_FILE (); - if (fname[0] == '/') - new_file->info.name = fname; - else - { - new_file->info.name = - libdw_alloc (dbg, char, 1, (dirarray[diridx].len + 1 - + fnamelen + 1)); - char *cp = new_file->info.name; - - if (dirarray[diridx].dir != NULL) - /* This value could be NULL in case the - DW_AT_comp_dir was not present. We - cannot do much in this case. Just - keep the file relative. */ - { - cp = stpcpy (cp, dirarray[diridx].dir); - *cp++ = '/'; - } - strcpy (cp, fname); - } - - new_file->info.mtime = mtime; - new_file->info.length = filelength; - } - break; + nfilelist++; + new_file->next = filelist; + filelist = new_file; + + if (fname[0] == '/') + new_file->info.name = fname; + else + { + /* Directory names are stored in a char *[ndirs] located + after the last Dwarf_Fileinfo_s. */ + size_t nfiles = (*filesp)->nfiles; + const char **dirarray + = (const char **) &((*filesp)->info[nfiles]); + + const char *dname = dirarray[diridx]; + size_t dnamelen = strlen (dname); + + new_file->info.name = + libdw_alloc (dbg, char, 1, (dnamelen + fnamelen + 2)); + char *cp = new_file->info.name; + + if (dname != NULL) + + /* This value could be NULL in case the + DW_AT_comp_dir was not present. We + cannot do much in this case. Just + keep the file relative. */ + + { + cp = stpcpy (cp, dname); + *cp++ = '/'; + } + strcpy (cp, fname); + } + + new_file->info.mtime = mtime; + new_file->info.length = filelength; + } + break; case DW_LNE_set_discriminator: /* Takes one ULEB128 parameter, the discriminator. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -861,14 +1017,14 @@ read_srclines (Dwarf *dbg, if (unlikely (linep >= lineendp)) goto invalid_data; get_uleb128 (state.function_name, linep, lineendp); - state.function_name += debug_str_offset; + state.function_name += lh.debug_str_offset; break; case DW_LNE_NVIDIA_set_function_name: if (unlikely (linep >= lineendp)) goto invalid_data; get_uleb128 (state.function_name, linep, lineendp); - state.function_name += debug_str_offset; + state.function_name += lh.debug_str_offset; break; default: @@ -886,7 +1042,7 @@ read_srclines (Dwarf *dbg, { case DW_LNS_copy: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; /* Add a new line with the current state machine values. */ @@ -902,7 +1058,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_advance_pc: /* Takes one uleb128 parameter which is added to the address. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -914,7 +1070,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_advance_line: /* Takes one sleb128 parameter which is added to the line. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -925,7 +1081,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_file: /* Takes one uleb128 parameter which is stored in file. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -936,7 +1092,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_column: /* Takes one uleb128 parameter which is stored in column. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -947,7 +1103,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_negate_stmt: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; state.is_stmt = 1 - state.is_stmt; @@ -955,7 +1111,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_basic_block: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; state.basic_block = true; @@ -963,19 +1119,19 @@ read_srclines (Dwarf *dbg, case DW_LNS_const_add_pc: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; - if (unlikely (line_range == 0)) + if (unlikely (lh.line_range == 0)) goto invalid_data; - advance_pc ((255 - opcode_base) / line_range); + advance_pc ((255 - lh.opcode_base) / lh.line_range); break; case DW_LNS_fixed_advance_pc: /* Takes one 16 bit parameter which is added to the address. */ - if (unlikely (standard_opcode_lengths[opcode] != 1) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1) || unlikely (lineendp - linep < 2)) goto invalid_data; @@ -985,7 +1141,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_prologue_end: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; state.prologue_end = true; @@ -993,7 +1149,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_epilogue_begin: /* Takes no argument. */ - if (unlikely (standard_opcode_lengths[opcode] != 0)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 0)) goto invalid_data; state.epilogue_begin = true; @@ -1001,7 +1157,7 @@ read_srclines (Dwarf *dbg, case DW_LNS_set_isa: /* Takes one uleb128 parameter which is stored in isa. */ - if (unlikely (standard_opcode_lengths[opcode] != 1)) + if (unlikely (lh.standard_opcode_lengths[opcode] != 1)) goto invalid_data; if (unlikely (linep >= lineendp)) @@ -1015,7 +1171,7 @@ read_srclines (Dwarf *dbg, /* This is a new opcode the generator but not we know about. Read the parameters associated with it but then discard everything. Read all the parameters for this opcode. */ - for (int n = standard_opcode_lengths[opcode]; n > 0; --n) + for (int n = lh.standard_opcode_lengths[opcode]; n > 0; --n) { if (unlikely (linep >= lineendp)) goto invalid_data; @@ -1027,32 +1183,49 @@ read_srclines (Dwarf *dbg, } } - /* Put all the files in an array. */ - Dwarf_Files *files = libdw_alloc (dbg, Dwarf_Files, - sizeof (Dwarf_Files) - + nfilelist * sizeof (Dwarf_Fileinfo) - + (ndirlist + 1) * sizeof (char *), - 1); - const char **dirs = (void *) &files->info[nfilelist]; - - struct filelist *fileslist = filelist; - files->nfiles = nfilelist; - for (size_t n = nfilelist; n > 0; n--) + /* Merge filesp with the files from DW_LNE_define_file, if any. */ + if (unlikely (filelist != NULL)) { - files->info[n - 1] = fileslist->info; - fileslist = fileslist->next; - } - assert (fileslist == NULL); + Dwarf_Files *prevfiles = *filesp; + size_t ndirs = prevfiles->ndirs; + size_t nprevfiles = prevfiles->nfiles; + size_t nnewfiles = nprevfiles + nfilelist; + + Dwarf_Files *newfiles + = libdw_alloc (dbg, Dwarf_Files, + sizeof (Dwarf_Files) + + nnewfiles * sizeof (Dwarf_Fileinfo) + + (ndirs + 1) * sizeof (char *), + 1); + + + /* Copy prevfiles to newfiles. */ + for (size_t n = 0; n < nprevfiles; n++) + newfiles->info[n] = prevfiles->info[n]; + + /* Add files from DW_LNE_define_file to newfiles. */ + struct filelist *fileslist = filelist; + for (size_t n = nfilelist; n > 0; n--) + { + newfiles->info[nprevfiles + n - 1] = fileslist->info; + fileslist = fileslist->next; + } - /* Put all the directory strings in an array. */ - files->ndirs = ndirlist; - for (unsigned int i = 0; i < ndirlist; ++i) - dirs[i] = dirarray[i].dir; - dirs[ndirlist] = NULL; + if (fileslist != NULL) + goto invalid_data; - /* Pass the file data structure to the caller. */ - if (filesp != NULL) - *filesp = files; + const char **newdirs = (void *) &newfiles->info[nnewfiles]; + const char **prevdirs = (void *) &prevfiles->info[nprevfiles]; + + /* Copy prevdirs to newdirs. */ + for (size_t n = 0; n < ndirs; n++) + newdirs[n] = prevdirs[n]; + + /* Update filesp. */ + newfiles->nfiles = nnewfiles; + newfiles->ndirs = prevfiles->ndirs; + *filesp = newfiles; + } size_t buf_size = (sizeof (Dwarf_Lines) + (sizeof (Dwarf_Line) * state.nlinelist)); @@ -1087,7 +1260,7 @@ read_srclines (Dwarf *dbg, for (size_t i = 0; i < state.nlinelist; ++i) { lines->info[i] = sortlines[i]->line; - lines->info[i].files = files; + lines->info[i].files = *filesp; } /* Make sure the highest address for the CU is marked as end_sequence. @@ -1102,8 +1275,12 @@ read_srclines (Dwarf *dbg, /* Success. */ res = 0; + goto out; + +invalid_data: + __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); - out: +out: /* Free malloced line records, if any. */ for (size_t i = MAX_STACK_LINES; i < state.nlinelist; i++) { @@ -1111,14 +1288,14 @@ read_srclines (Dwarf *dbg, free (state.linelist); state.linelist = ll; } - if (dirarray != dirstack) - free (dirarray); - for (size_t i = MAX_STACK_FILES; i < nfilelist; i++) - { - struct filelist *fl = filelist->next; - free (filelist); - filelist = fl; - } + + /* Free file records from DW_LNE_define_file, if any. */ + for (size_t i = 0; i < nfilelist; i++) + { + struct filelist *fl = filelist->next; + free (filelist); + filelist = fl; + } return res; } @@ -1137,17 +1314,17 @@ files_lines_compare (const void *p1, const void *p2) return 0; } -int -internal_function -__libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, - const char *comp_dir, unsigned address_size, - Dwarf_Lines **linesp, Dwarf_Files **filesp) +static int +get_lines_or_files (Dwarf *dbg, Dwarf_Off debug_line_offset, + const char *comp_dir, unsigned address_size, + Dwarf_Lines **linesp, Dwarf_Files **filesp) { struct files_lines_s fake = { .debug_line_offset = debug_line_offset }; struct files_lines_s **found = tfind (&fake, &dbg->files_lines, files_lines_compare); if (found == NULL) { + /* This .debug_line is being read for the first time. */ Elf_Data *data = __libdw_checked_get_data (dbg, IDX_debug_line); if (data == NULL || __libdw_offset_in_section (dbg, IDX_debug_line, @@ -1160,8 +1337,19 @@ __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, struct files_lines_s *node = libdw_alloc (dbg, struct files_lines_s, sizeof *node, 1); - if (read_srclines (dbg, linep, lineendp, comp_dir, address_size, - &node->lines, &node->files) != 0) + /* Srcfiles will be read but srclines might not. Set lines here + to avoid possible uninitialized value errors. */ + node->lines = NULL; + + /* If linesp is NULL then read srcfiles without reading srclines. */ + if (linesp == NULL) + { + if (read_srcfiles (dbg, linep, lineendp, comp_dir, address_size, + NULL, &node->files) != 0) + return -1; + } + else if (read_srclines (dbg, linep, lineendp, comp_dir, address_size, + &node->lines, &node->files, false) != 0) return -1; node->debug_line_offset = debug_line_offset; @@ -1173,6 +1361,35 @@ __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, return -1; } } + else if (*found != NULL + && (*found)->files != NULL + && (*found)->lines == NULL) + { + /* Srcfiles were already read from this .debug_line. Now read + srclines. */ + Elf_Data *data = __libdw_checked_get_data (dbg, IDX_debug_line); + if (data == NULL + || __libdw_offset_in_section (dbg, IDX_debug_line, + debug_line_offset, 1) != 0) + return -1; + + const unsigned char *linep = data->d_buf + debug_line_offset; + const unsigned char *lineendp = data->d_buf + data->d_size; + + struct files_lines_s *node = *found; + + if (read_srclines (dbg, linep, lineendp, comp_dir, address_size, + &node->lines, &node->files, true) != 0) + return -1; + } + else if (*found != NULL + && (*found)->files == NULL + && (*found)->lines != NULL) + { + /* If srclines were read then srcfiles should have also been read. */ + __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); + return -1; + } if (linesp != NULL) *linesp = (*found)->lines; @@ -1183,6 +1400,26 @@ __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, return 0; } +int +internal_function +__libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, + const char *comp_dir, unsigned address_size, + Dwarf_Lines **linesp, Dwarf_Files **filesp) +{ + return get_lines_or_files (dbg, debug_line_offset, comp_dir, + address_size, linesp, filesp); +} + +int +internal_function +__libdw_getsrcfiles (Dwarf *dbg, Dwarf_Off debug_line_offset, + const char *comp_dir, unsigned address_size, + Dwarf_Files **filesp) +{ + return get_lines_or_files (dbg, debug_line_offset, comp_dir, + address_size, NULL, filesp); +} + /* Get the compilation directory, if any is set. */ const char * __libdw_getcompdir (Dwarf_Die *cudie) diff --git a/libdw/dwarf_macro_getsrcfiles.c b/libdw/dwarf_macro_getsrcfiles.c index 11c587af..5e02935d 100644 --- a/libdw/dwarf_macro_getsrcfiles.c +++ b/libdw/dwarf_macro_getsrcfiles.c @@ -74,8 +74,8 @@ dwarf_macro_getsrcfiles (Dwarf *dbg, Dwarf_Macro *macro, the same unit through dwarf_getsrcfiles, and the file names will be broken. */ - if (__libdw_getsrclines (table->dbg, line_offset, table->comp_dir, - table->address_size, NULL, &table->files) < 0) + if (__libdw_getsrcfiles (table->dbg, line_offset, table->comp_dir, + table->address_size, &table->files) < 0) table->files = (void *) -1; } diff --git a/libdw/libdwP.h b/libdw/libdwP.h index c1c84ed3..e55ff50a 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -1108,6 +1108,16 @@ int __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, internal_function __nonnull_attribute__ (1); +/* Load .debug_line unit at DEBUG_LINE_OFFSET. COMP_DIR is a value of + DW_AT_comp_dir or NULL if that attribute is not available. Caches + the loaded unit and set *FILESP with loaded information. Returns 0 + for success or a negative value for failure. */ +int __libdw_getsrcfiles (Dwarf *dbg, Dwarf_Off debug_line_offset, + const char *comp_dir, unsigned address_size, + Dwarf_Files **filesp) + internal_function + __nonnull_attribute__ (1); + /* Load and return value of DW_AT_comp_dir from CUDIE. */ const char *__libdw_getcompdir (Dwarf_Die *cudie); diff --git a/tests/.gitignore b/tests/.gitignore index 0289959d..e40ad238 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -74,6 +74,7 @@ /funcscopes /get-aranges /get-files +/get-files-define_file /get-lines /get-pubnames /get-units-invalid diff --git a/tests/Makefile.am b/tests/Makefile.am index 9141074f..7b016dc8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -35,7 +35,7 @@ endif check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ showptable update1 update2 update3 update4 test-nlist \ show-die-info get-files next-files get-lines next-lines \ - get-pubnames \ + get-pubnames get-files-define-file \ get-aranges allfcts line2addr addrscopes funcscopes \ show-abbrev hash newscn ecp dwflmodtest \ find-prologues funcretval allregs rdwrmmap \ @@ -646,7 +646,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ testfile-dwp-5-cu-index-overflow.dwp.bz2 \ testfile-dwp-4-cu-index-overflow.bz2 \ testfile-dwp-4-cu-index-overflow.dwp.bz2 \ - testfile-dwp-cu-index-overflow.source + testfile-dwp-cu-index-overflow.source \ + testfile-define-file.bz2 if USE_VALGRIND @@ -725,6 +726,7 @@ show_abbrev_LDADD = $(libdw) $(libelf) get_lines_LDADD = $(libdw) $(libelf) next_lines_LDADD = $(libdw) $(libelf) get_files_LDADD = $(libdw) $(libelf) +get_files_define_file_LDADD = $(libdw) $(libelf) next_files_LDADD = $(libdw) $(libelf) get_aranges_LDADD = $(libdw) $(libelf) allfcts_LDADD = $(libdw) $(libelf) diff --git a/tests/get-files-define-file.c b/tests/get-files-define-file.c new file mode 100644 index 00000000..583f9852 --- /dev/null +++ b/tests/get-files-define-file.c @@ -0,0 +1,162 @@ +/* Copyright (C) 2002, 2004, 2005, 2007 Red Hat, Inc. + This file is part of elfutils. + Written by Ulrich Drepper , 2002. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include ELFUTILS_HEADER(dw) +#include +#include +#include "../libdw/libdwP.h" + +static void +print_dirs_and_files (Dwarf_Files *files, const char *const *dirs, + size_t nfiles, size_t ndirs) +{ + if (dirs[0] == NULL) + puts (" dirs[0] = (null)"); + else + printf (" dirs[0] = \"%s\"\n", dirs[0]); + for (size_t i = 1; i < ndirs; ++i) + printf (" dirs[%zu] = \"%s\"\n", i, dirs[i]); + + for (size_t i = 0; i < nfiles; ++i) + printf (" file[%zu] = \"%s\"\n", i, + dwarf_filesrc (files, i, NULL, NULL)); +} + + +int +main (int argc, char *argv[]) +{ + int result = 0; + int cnt = argc - 1; + + int fd = open (argv[cnt], O_RDONLY); + + Dwarf *dbg = dwarf_begin (fd, DWARF_C_READ); + if (dbg == NULL) + { + printf ("%s not usable\n", argv[cnt]); + result = 1; + if (fd != -1) + close (fd); + goto out; + } + + Dwarf_Off o = 0; + Dwarf_Off ncu; + size_t cuhl; + + /* Just inspect the first CU. */ + if (dwarf_nextcu (dbg, o, &ncu, &cuhl, NULL, NULL, NULL) != 0) + { + printf ("%s: cannot get CU\n", argv[cnt]); + result = 1; + goto out; + } + + Dwarf_Die die_mem; + Dwarf_Die *die = dwarf_offdie (dbg, o + cuhl, &die_mem); + + if (die == NULL) + { + printf ("%s: cannot get CU die\n", argv[cnt]); + result = 1; + goto out; + } + + Dwarf_Files *files; + size_t nfiles; + + /* The files from DW_LNE_define_file should not be included + until dwarf_getsrclines is called. */ + if (dwarf_getsrcfiles (die, &files, &nfiles) != 0) + { + printf ("%s: cannot get files\n", argv[cnt]); + result = 1; + goto out; + } + + if (die->cu->lines != NULL) + { + printf ("%s: dwarf_getsrcfiles should not get lines\n", argv[cnt]); + result = 1; + goto out; + } + + const char *const *dirs; + size_t ndirs; + if (dwarf_getsrcdirs (files, &dirs, &ndirs) != 0) + { + printf ("%s: cannot get include directories\n", argv[cnt]); + result = 1; + goto out; + } + + /* Print file info without files from DW_LNE_define_file. */ + print_dirs_and_files (files, dirs, nfiles, ndirs); + + Dwarf_Lines *lines; + size_t nlines; + + /* Reading the line program should add the new files. */ + if (dwarf_getsrclines (die, &lines, &nlines) != 0) + { + printf ("%s: cannot get lines\n", argv[cnt]); + result = 1; + goto out; + } + + Dwarf_Files *updated_files; + size_t num_updated_files; + + /* Get the new files. */ + if (dwarf_getsrcfiles (die, &updated_files, &num_updated_files) != 0) + { + printf ("%s: cannot get files\n", argv[cnt]); + result = 1; + goto out; + } + + const char *const *updated_dirs; + size_t num_updated_dirs; + + /* The dirs shouldn't change but verify that getsrcdirs still works. */ + if (dwarf_getsrcdirs (updated_files, &updated_dirs, &num_updated_dirs) != 0) + { + printf ("%s: cannot get include directories\n", argv[cnt]); + result = 1; + goto out; + } + + /* Verify that we didn't invalidate the old file info. */ + print_dirs_and_files (files, dirs, nfiles, ndirs); + + /* Print all files including those from DW_LNE_define_file. */ + print_dirs_and_files (updated_files, updated_dirs, + num_updated_files, num_updated_dirs); + +out: + dwarf_end (dbg); + close (fd); + + return result; +} diff --git a/tests/get-files.c b/tests/get-files.c index 04091733..fa65aa93 100644 --- a/tests/get-files.c +++ b/tests/get-files.c @@ -24,6 +24,7 @@ #include ELFUTILS_HEADER(dw) #include #include +#include "../libdw/libdwP.h" int @@ -76,6 +77,13 @@ main (int argc, char *argv[]) break; } + if (die->cu->lines != NULL) + { + printf ("%s: dwarf_getsrcfiles should not get lines\n", argv[cnt]); + result = 1; + break; + } + const char *const *dirs; size_t ndirs; if (dwarf_getsrcdirs (files, &dirs, &ndirs) != 0) diff --git a/tests/get-lines.c b/tests/get-lines.c index 188d0162..77fb3c54 100644 --- a/tests/get-lines.c +++ b/tests/get-lines.c @@ -26,6 +26,7 @@ #include #include #include +#include "../libdw/libdwP.h" int @@ -69,6 +70,24 @@ main (int argc, char *argv[]) } old_cuoff = cuoff; + Dwarf_Files *files; + size_t nfiles; + + /* Get files first to test that lines are read separately. */ + if (dwarf_getsrcfiles (&die, &files, &nfiles) != 0) + { + printf ("%s: cannot get files\n", argv[cnt]); + result = 1; + break; + } + + if (die.cu->lines != NULL) + { + printf ("%s: dwarf_getsrcfiles should not get lines\n", argv[cnt]); + result = 1; + break; + } + Dwarf_Lines *lb; size_t nlb; if (dwarf_getsrclines (&die, &lb, &nlb) != 0) @@ -103,7 +122,6 @@ main (int argc, char *argv[]) /* Getting the file path through the Dwarf_Files should result in the same path. */ - Dwarf_Files *files; size_t idx; if (dwarf_line_file (l, &files, &idx) != 0) { diff --git a/tests/run-get-files.sh b/tests/run-get-files.sh index 1306544d..c2bc01bf 100755 --- a/tests/run-get-files.sh +++ b/tests/run-get-files.sh @@ -18,7 +18,7 @@ . $srcdir/test-subr.sh -testfiles testfile testfile2 +testfiles testfile testfile2 testfile-define-file testrun_compare ${abs_builddir}/get-files testfile testfile2 <<\EOF cuhl = 11, o = 0, asz = 4, osz = 4, ncu = 191 @@ -245,4 +245,67 @@ cuhl = 11, o = 0, asz = 8, osz = 4, ncu = 857 file[3] = "/usr/include/stdc-predef.h" EOF +tempfiles files define-files.out get-files-define-file.out + +cat > files <<\EOF + dirs[0] = "session" + dirs[1] = "/home/wcohen/minimal_mod" + dirs[2] = "include/asm" + dirs[3] = "include/linux" + dirs[4] = "include/asm-generic" + file[0] = "???" + file[1] = "/home/wcohen/minimal_mod/minimal_mod.c" + file[2] = "include/asm/gcc_intrin.h" + file[3] = "include/linux/kernel.h" + file[4] = "include/asm/processor.h" + file[5] = "include/asm/types.h" + file[6] = "include/asm/ptrace.h" + file[7] = "include/linux/sched.h" + file[8] = "include/asm/thread_info.h" + file[9] = "include/linux/thread_info.h" + file[10] = "include/asm/atomic.h" + file[11] = "include/linux/list.h" + file[12] = "include/linux/cpumask.h" + file[13] = "include/linux/rbtree.h" + file[14] = "include/asm/page.h" + file[15] = "include/linux/rwsem.h" + file[16] = "include/asm/rwsem.h" + file[17] = "include/asm/spinlock.h" + file[18] = "include/linux/completion.h" + file[19] = "include/linux/wait.h" + file[20] = "include/linux/aio.h" + file[21] = "include/linux/workqueue.h" + file[22] = "include/linux/timer.h" + file[23] = "include/linux/types.h" + file[24] = "include/asm/posix_types.h" + file[25] = "include/linux/pid.h" + file[26] = "include/linux/time.h" + file[27] = "include/linux/capability.h" + file[28] = "include/linux/signal.h" + file[29] = "include/linux/resource.h" + file[30] = "include/linux/sem.h" + file[31] = "include/asm/fpu.h" + file[32] = "include/linux/fs_struct.h" + file[33] = "include/asm/signal.h" + file[34] = "include/asm/siginfo.h" + file[35] = "include/asm-generic/siginfo.h" + file[36] = "include/asm/nodedata.h" + file[37] = "include/linux/mmzone.h" + file[38] = "include/linux/jiffies.h" + file[39] = "include/asm/io.h" + file[40] = "include/asm/machvec.h" + file[41] = "include/asm/smp.h" + file[42] = "include/asm/numa.h" + file[43] = "include/linux/slab.h" +EOF + +# Files should be printed 3 times, followed by the files from DW_LNE_define_file +cat files > define-files.out +cat files >> define-files.out +cat files >> define-files.out +echo ' file[44] = "include/asm/abc.c"' >> define-files.out +echo ' file[45] = "include/linux/01.c"' >> define-files.out + +cat define-files.out | testrun_compare ${abs_builddir}/get-files-define-file testfile-define-file + exit 0 diff --git a/tests/testfile-define-file.bz2 b/tests/testfile-define-file.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..32ee7b76545bd6e2afca786e8f0314df2e2d326b GIT binary patch literal 24910 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

w&(WPDEe}^ukC;~GH6crU`2?xon3UfTcX@ux@ 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