Skip to content

Commit 6d48bbf

Browse files
committed
Support note NT_FILE for locating files.
Martin Milata: ------------------------------------------------------------------------------ RFE: dwfl_core_file_report: use NT_FILE core note if the link_map chain is broken https://bugzilla.redhat.com/show_bug.cgi?id=1129777 The dwfl_core_file_report function follows dynamic linker's link_map chain in order to determine the shared libraries used by the executable. As this data structure is located in writable memory it can be overwritten by garbage, which is sometimes the case. abrt/satyr#127 (comment) Since version 3.7 (commit 2aa362c49), Linux kernel adds NT_FILE note to core files which contains the files mapped by the process, including shared libraries. ------------------------------------------------------------------------------ dwfl_core_file_report now tries to fall back on NT_FILE if the link_map chain is broken. elfutils would already find the appropriate binary file from /usr/lib/debug/.build-id/ symbolic links. But those symbolic links do not have to be present on the system while NT_FILE still points to the correct binaries. Filenames from the note NT_FILE are used only if link_map filenames failed to locate matching binaries. tests/test-core.core.bz2 had to have its NT_FILE disabled as run-unstrip-n.sh otherwise FAILs: FAIL: 0x7f67f2aaf000+0x202000 - . - /home/jkratoch/redhat/elfutils-libregr/test-core-lib.so PASS: 0x7f67f2aaf000+0x202000 - . - test-core-lib.so As test-core-lib.so is found in link_map but it is not present on the disk elfutils now chooses the more reliable filename from NT_FILE (although that filename is also not found on the disk). Updating the expected text would be also sufficient. libdwfl/ 2014-09-26 Jan Kratochvil <[email protected]> Support NT_FILE for locating files. * core-file.c (dwfl_core_file_report): New variables note_file and note_file_size, set them and pass them to dwfl_segment_report_module. * dwfl_segment_report_module.c: Include common.h and fcntl.h. (buf_has_data, buf_read_ulong, handle_file_note): New functions. (invalid_elf): New function from code of dwfl_segment_report_module. (dwfl_segment_report_module): Add parameters note_file and note_file_size. New variables elf and fd, clean them up in finish. Move some code to invalid_elf. Call handle_file_note, if it found a name verify the file by invalid_elf. Protect elf and fd against cleanup by finish if we found the file for new Dwfl_Module. * libdwflP.h (dwfl_segment_report_module): Add parameters note_file and note_file_size. tests/ 2014-09-26 Jan Kratochvil <[email protected]> Support NT_FILE for locating files. * Makefile.am (TESTS): Add run-linkmap-cut.sh. (EXTRA_DIST): Add run-linkmap-cut.sh, linkmap-cut-lib.so.bz2, linkmap-cut.bz2 and linkmap-cut.core.bz2 . * linkmap-cut-lib.so.bz2: New file. * linkmap-cut.bz2: New file. * linkmap-cut.core.bz2: New file. * run-linkmap-cut.sh: New file. * run-unstrip-n.sh: Update its expected output. Signed-off-by: Jan Kratochvil <[email protected]>
1 parent ff61cf1 commit 6d48bbf

11 files changed

+268
-37
lines changed

Diff for: libdwfl/ChangeLog

+16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
2014-09-26 Jan Kratochvil <[email protected]>
2+
3+
Support NT_FILE for locating files.
4+
* core-file.c (dwfl_core_file_report): New variables note_file and
5+
note_file_size, set them and pass them to dwfl_segment_report_module.
6+
* dwfl_segment_report_module.c: Include common.h and fcntl.h.
7+
(buf_has_data, buf_read_ulong, handle_file_note): New functions.
8+
(invalid_elf): New function from code of dwfl_segment_report_module.
9+
(dwfl_segment_report_module): Add parameters note_file and
10+
note_file_size. New variables elf and fd, clean them up in finish.
11+
Move some code to invalid_elf. Call handle_file_note, if it found
12+
a name verify the file by invalid_elf. Protect elf and fd against
13+
cleanup by finish if we found the file for new Dwfl_Module.
14+
* libdwflP.h (dwfl_segment_report_module): Add parameters note_file and
15+
note_file_size.
16+
117
2014-09-23 Mark Wielaard <[email protected]>
218

319
* dwfl_segment_report_module.c (dwfl_segment_report_module):

Diff for: libdwfl/core-file.c

+14-5
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,9 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
451451
/* Next, we should follow the chain from DT_DEBUG. */
452452

453453
const void *auxv = NULL;
454+
const void *note_file = NULL;
454455
size_t auxv_size = 0;
456+
size_t note_file_size = 0;
455457
if (likely (notes_phdr.p_type == PT_NOTE))
456458
{
457459
/* PT_NOTE -> NT_AUXV -> AT_PHDR -> PT_DYNAMIC -> DT_DEBUG */
@@ -468,13 +470,19 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
468470
size_t desc_pos;
469471
while ((pos = gelf_getnote (notes, pos, &nhdr,
470472
&name_pos, &desc_pos)) > 0)
471-
if (nhdr.n_type == NT_AUXV
472-
&& nhdr.n_namesz == sizeof "CORE"
473+
if (nhdr.n_namesz == sizeof "CORE"
473474
&& !memcmp (notes->d_buf + name_pos, "CORE", sizeof "CORE"))
474475
{
475-
auxv = notes->d_buf + desc_pos;
476-
auxv_size = nhdr.n_descsz;
477-
break;
476+
if (nhdr.n_type == NT_AUXV)
477+
{
478+
auxv = notes->d_buf + desc_pos;
479+
auxv_size = nhdr.n_descsz;
480+
}
481+
if (nhdr.n_type == NT_FILE)
482+
{
483+
note_file = notes->d_buf + desc_pos;
484+
note_file_size = nhdr.n_descsz;
485+
}
478486
}
479487
}
480488
}
@@ -498,6 +506,7 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
498506
int seg = dwfl_segment_report_module (dwfl, ndx, NULL,
499507
&dwfl_elf_phdr_memory_callback, elf,
500508
core_file_read_eagerly, elf,
509+
note_file, note_file_size,
501510
&r_debug_info);
502511
if (unlikely (seg < 0))
503512
{

Diff for: libdwfl/dwfl_segment_report_module.c

+187-29
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "../libelf/libelfP.h" /* For NOTE_ALIGN. */
3131
#undef _
3232
#include "libdwflP.h"
33+
#include "common.h"
3334

3435
#include <elf.h>
3536
#include <gelf.h>
@@ -38,6 +39,7 @@
3839
#include <alloca.h>
3940
#include <endian.h>
4041
#include <unistd.h>
42+
#include <fcntl.h>
4143

4244

4345
/* A good size for the initial read from memory, if it's not too costly.
@@ -79,12 +81,160 @@ addr_segndx (Dwfl *dwfl, size_t segment, GElf_Addr addr, bool next)
7981
return ndx;
8082
}
8183

84+
/* Return whether there is SZ bytes available at PTR till END. */
85+
86+
static bool
87+
buf_has_data (const void *ptr, const void *end, size_t sz)
88+
{
89+
return ptr < end && (size_t) (end - ptr) >= sz;
90+
}
91+
92+
/* Read SZ bytes into *RETP from *PTRP (limited by END) in format EI_DATA.
93+
Function comes from src/readelf.c . */
94+
95+
static bool
96+
buf_read_ulong (unsigned char ei_data, size_t sz,
97+
const void **ptrp, const void *end, uint64_t *retp)
98+
{
99+
if (! buf_has_data (*ptrp, end, sz))
100+
return false;
101+
102+
union
103+
{
104+
uint64_t u64;
105+
uint32_t u32;
106+
} u;
107+
108+
memcpy (&u, *ptrp, sz);
109+
(*ptrp) += sz;
110+
111+
if (retp == NULL)
112+
return true;
113+
114+
if (MY_ELFDATA != ei_data)
115+
{
116+
if (sz == 4)
117+
CONVERT (u.u32);
118+
else
119+
CONVERT (u.u64);
120+
}
121+
if (sz == 4)
122+
*retp = u.u32;
123+
else
124+
*retp = u.u64;
125+
return true;
126+
}
127+
128+
/* Try to find matching entry for module from address MODULE_START to
129+
MODULE_END in NT_FILE note located at NOTE_FILE of NOTE_FILE_SIZE
130+
bytes in format EI_CLASS and EI_DATA. */
131+
132+
static const char *
133+
handle_file_note (GElf_Addr module_start, GElf_Addr module_end,
134+
unsigned char ei_class, unsigned char ei_data,
135+
const void *note_file, size_t note_file_size)
136+
{
137+
if (note_file == NULL)
138+
return NULL;
139+
140+
size_t sz;
141+
switch (ei_class)
142+
{
143+
case ELFCLASS32:
144+
sz = 4;
145+
break;
146+
case ELFCLASS64:
147+
sz = 8;
148+
break;
149+
default:
150+
return NULL;
151+
}
152+
153+
const void *ptr = note_file;
154+
const void *end = note_file + note_file_size;
155+
uint64_t count;
156+
if (! buf_read_ulong (ei_data, sz, &ptr, end, &count))
157+
return NULL;
158+
if (! buf_read_ulong (ei_data, sz, &ptr, end, NULL)) // page_size
159+
return NULL;
160+
161+
/* Where file names are stored. */
162+
const char *fptr = ptr + 3 * count * sz;
163+
164+
ssize_t firstix = -1;
165+
ssize_t lastix = -1;
166+
for (size_t mix = 0; mix < count; mix++)
167+
{
168+
uint64_t mstart, mend, moffset;
169+
if (! buf_read_ulong (ei_data, sz, &ptr, fptr, &mstart)
170+
|| ! buf_read_ulong (ei_data, sz, &ptr, fptr, &mend)
171+
|| ! buf_read_ulong (ei_data, sz, &ptr, fptr, &moffset))
172+
return NULL;
173+
if (mstart == module_start && moffset == 0)
174+
firstix = lastix = mix;
175+
if (firstix != -1 && mstart < module_end)
176+
lastix = mix;
177+
if (mend >= module_end)
178+
break;
179+
}
180+
if (firstix == -1)
181+
return NULL;
182+
183+
const char *retval = NULL;
184+
for (ssize_t mix = 0; mix <= lastix; mix++)
185+
{
186+
const char *fnext = memchr (fptr, 0, (const char *) end - fptr);
187+
if (fnext == NULL)
188+
return NULL;
189+
if (mix == firstix)
190+
retval = fptr;
191+
if (firstix < mix && mix <= lastix && strcmp (fptr, retval) != 0)
192+
return NULL;
193+
fptr = fnext + 1;
194+
}
195+
return retval;
196+
}
197+
198+
/* Return true iff we are certain ELF cannot match BUILD_ID of
199+
BUILD_ID_LEN bytes. Pass DISK_FILE_HAS_BUILD_ID as false if it is
200+
certain ELF does not contain build-id (it is only a performance hit
201+
to pass it always as true). */
202+
203+
static bool
204+
invalid_elf (Elf *elf, bool disk_file_has_build_id,
205+
const void *build_id, size_t build_id_len)
206+
{
207+
if (! disk_file_has_build_id && build_id_len > 0)
208+
{
209+
/* Module found in segments with build-id is more reliable
210+
than a module found via DT_DEBUG on disk without any
211+
build-id. */
212+
return true;
213+
}
214+
if (disk_file_has_build_id && build_id_len > 0)
215+
{
216+
const void *elf_build_id;
217+
ssize_t elf_build_id_len;
218+
219+
/* If there is a build id in the elf file, check it. */
220+
elf_build_id_len = INTUSE(dwelf_elf_gnu_build_id) (elf, &elf_build_id);
221+
if (elf_build_id_len > 0)
222+
{
223+
if (build_id_len != (size_t) elf_build_id_len
224+
|| memcmp (build_id, elf_build_id, build_id_len) != 0)
225+
return true;
226+
}
227+
}
228+
return false;
229+
}
230+
82231
int
83232
dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
84233
Dwfl_Memory_Callback *memory_callback,
85234
void *memory_callback_arg,
86235
Dwfl_Module_Callback *read_eagerly,
87236
void *read_eagerly_arg,
237+
const void *note_file, size_t note_file_size,
88238
const struct r_debug_info *r_debug_info)
89239
{
90240
size_t segment = ndx;
@@ -121,10 +271,16 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
121271

122272
void *buffer = NULL;
123273
size_t buffer_available = INITIAL_READ;
274+
Elf *elf = NULL;
275+
int fd = -1;
124276

125277
inline int finish (void)
126278
{
127279
release_buffer (&buffer, &buffer_available);
280+
if (elf != NULL)
281+
elf_end (elf);
282+
if (fd != -1)
283+
close (fd);
128284
return ndx;
129285
}
130286

@@ -486,32 +642,9 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
486642
if ((module_end > module->start && module_start < module->end)
487643
|| dyn_vaddr == module->l_ld)
488644
{
489-
bool close_elf = false;
490-
if (! module->disk_file_has_build_id && build_id_len > 0)
491-
{
492-
/* Module found in segments with build-id is more reliable
493-
than a module found via DT_DEBUG on disk without any
494-
build-id. */
495-
if (module->elf != NULL)
496-
close_elf = true;
497-
}
498645
if (module->elf != NULL
499-
&& module->disk_file_has_build_id && build_id_len > 0)
500-
{
501-
const void *elf_build_id;
502-
ssize_t elf_build_id_len;
503-
504-
/* If there is a build id in the elf file, check it. */
505-
elf_build_id_len = INTUSE(dwelf_elf_gnu_build_id) (module->elf,
506-
&elf_build_id);
507-
if (elf_build_id_len > 0)
508-
{
509-
if (build_id_len != (size_t) elf_build_id_len
510-
|| memcmp (build_id, elf_build_id, build_id_len) != 0)
511-
close_elf = true;
512-
}
513-
}
514-
if (close_elf)
646+
&& invalid_elf (module->elf, module->disk_file_has_build_id,
647+
build_id, build_id_len))
515648
{
516649
elf_end (module->elf);
517650
close (module->fd);
@@ -532,6 +665,29 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
532665
}
533666
}
534667

668+
const char *file_note_name = handle_file_note (module_start, module_end,
669+
ei_class, ei_data,
670+
note_file, note_file_size);
671+
if (file_note_name)
672+
{
673+
name = file_note_name;
674+
name_is_final = true;
675+
bool invalid = false;
676+
fd = open64 (name, O_RDONLY);
677+
if (fd >= 0)
678+
{
679+
Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false);
680+
if (error == DWFL_E_NOERROR)
681+
invalid = invalid_elf (elf, true /* disk_file_has_build_id */,
682+
build_id, build_id_len);
683+
}
684+
if (invalid)
685+
{
686+
free (build_id);
687+
return finish ();
688+
}
689+
}
690+
535691
/* Our return value now says to skip the segments contained
536692
within the module. */
537693
ndx = addr_segndx (dwfl, segment, module_end, true);
@@ -686,10 +842,10 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
686842
: dynstr_vaddr + dynstrsz - start);
687843
const GElf_Off whole = MAX (file_trimmed_end, shdrs_end);
688844

689-
Elf *elf = NULL;
690-
if ((*read_eagerly) (MODCB_ARGS (mod), &buffer, &buffer_available,
691-
cost, worthwhile, whole, contiguous,
692-
read_eagerly_arg, &elf)
845+
if (elf == NULL
846+
&& (*read_eagerly) (MODCB_ARGS (mod), &buffer, &buffer_available,
847+
cost, worthwhile, whole, contiguous,
848+
read_eagerly_arg, &elf)
693849
&& elf == NULL)
694850
{
695851
/* The caller wants to read the whole file in right now, but hasn't
@@ -751,6 +907,8 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
751907
{
752908
/* Install the file in the module. */
753909
mod->main.elf = elf;
910+
elf = NULL;
911+
fd = -1;
754912
mod->main.vaddr = module_start - bias;
755913
mod->main.address_sync = module_address_sync;
756914
mod->main_bias = bias;

Diff for: libdwfl/libdwflP.h

+2
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,8 @@ extern int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
660660
void *memory_callback_arg,
661661
Dwfl_Module_Callback *read_eagerly,
662662
void *read_eagerly_arg,
663+
const void *note_file,
664+
size_t note_file_size,
663665
const struct r_debug_info *r_debug_info);
664666

665667
/* Report a module for entry in the dynamic linker's struct link_map list.

Diff for: tests/ChangeLog

+12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
2014-09-26 Jan Kratochvil <[email protected]>
2+
3+
Support NT_FILE for locating files.
4+
* Makefile.am (TESTS): Add run-linkmap-cut.sh.
5+
(EXTRA_DIST): Add run-linkmap-cut.sh, linkmap-cut-lib.so.bz2,
6+
linkmap-cut.bz2 and linkmap-cut.core.bz2 .
7+
* linkmap-cut-lib.so.bz2: New file.
8+
* linkmap-cut.bz2: New file.
9+
* linkmap-cut.core.bz2: New file.
10+
* run-linkmap-cut.sh: New file.
11+
* run-unstrip-n.sh: Update its expected output.
12+
113
2014-08-28 Jan Kratochvil <[email protected]>
214

315
* Makefile.am (check_PROGRAMS): Add deleted and deleted-lib.so.

Diff for: tests/Makefile.am

+4-2
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
109109
run-backtrace-core-s390x.sh run-backtrace-core-s390.sh \
110110
run-backtrace-core-aarch64.sh \
111111
run-backtrace-demangle.sh run-stack-d-test.sh run-stack-i-test.sh \
112-
run-readelf-dwz-multi.sh run-allfcts-multi.sh run-deleted.sh
112+
run-readelf-dwz-multi.sh run-allfcts-multi.sh run-deleted.sh \
113+
run-linkmap-cut.sh
113114

114115
if !BIARCH
115116
export ELFUTILS_DISABLE_BIARCH = 1
@@ -272,7 +273,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
272273
run-stack-d-test.sh run-stack-i-test.sh \
273274
testfiledwarfinlines.bz2 testfiledwarfinlines.core.bz2 \
274275
run-readelf-zdebug.sh testfile-debug.bz2 testfile-zdebug.bz2 \
275-
run-deleted.sh
276+
run-deleted.sh run-linkmap-cut.sh linkmap-cut-lib.so.bz2 \
277+
linkmap-cut.bz2 linkmap-cut.core.bz2
276278

277279
if USE_VALGRIND
278280
valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'

Diff for: tests/linkmap-cut-lib.so.bz2

2.1 KB
Binary file not shown.

Diff for: tests/linkmap-cut.bz2

2.57 KB
Binary file not shown.

Diff for: tests/linkmap-cut.core.bz2

20.8 KB
Binary file not shown.

0 commit comments

Comments
 (0)