2 * Copyright 2016, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
15 #include <AutoDeleter.h>
17 #include "ElfSymbolLookup.h"
21 static const size_t kMaxNotesSize
= 10 * 1024 * 1024;
24 // pragma mark - CoreFileTeamInfo
27 CoreFileTeamInfo::CoreFileTeamInfo()
38 CoreFileTeamInfo::Init(int32 id
, int32 uid
, int32 gid
, const BString
& args
)
47 // pragma mark - CoreFileAreaInfo
50 CoreFileAreaInfo::CoreFileAreaInfo(ElfSegment
* segment
, int32 id
,
51 uint64 baseAddress
, uint64 size
, uint64 ramSize
, uint32 locking
,
52 uint32 protection
, const BString
& name
)
55 fBaseAddress(baseAddress
),
59 fProtection(protection
),
65 // pragma mark - CoreFileImageInfo
68 CoreFileImageInfo::CoreFileImageInfo(int32 id
, int32 type
, uint64 initRoutine
,
69 uint64 termRoutine
, uint64 textBase
, uint64 textSize
, int64 textDelta
,
70 uint64 dataBase
, uint64 dataSize
, int32 deviceId
, int64 nodeId
,
71 uint64 symbolTable
, uint64 symbolHash
, uint64 stringTable
,
72 CoreFileAreaInfo
* textArea
, CoreFileAreaInfo
* dataArea
, const BString
& name
)
76 fInitRoutine(initRoutine
),
77 fTermRoutine(termRoutine
),
80 fTextDelta(textDelta
),
85 fSymbolTable(symbolTable
),
86 fSymbolHash(symbolHash
),
87 fStringTable(stringTable
),
96 CoreFileImageInfo::~CoreFileImageInfo()
103 CoreFileImageInfo::SetSymbolsInfo(CoreFileSymbolsInfo
* symbolsInfo
)
105 if (fSymbolsInfo
!= NULL
)
108 fSymbolsInfo
= symbolsInfo
;
112 // pragma mark - CoreFileSymbolsInfo
114 CoreFileSymbolsInfo::CoreFileSymbolsInfo()
119 fSymbolTableEntrySize(0),
125 CoreFileSymbolsInfo::~CoreFileSymbolsInfo()
133 CoreFileSymbolsInfo::Init(const void* symbolTable
, uint32 symbolCount
,
134 uint32 symbolTableEntrySize
, const char* stringTable
,
135 uint32 stringTableSize
)
137 fSymbolTable
= malloc(symbolCount
* symbolTableEntrySize
);
138 fStringTable
= (char*)malloc(stringTableSize
);
140 if (fSymbolTable
== NULL
|| fStringTable
== NULL
)
143 memcpy(fSymbolTable
, symbolTable
, symbolCount
* symbolTableEntrySize
);
144 memcpy(fStringTable
, stringTable
, stringTableSize
);
146 fSymbolCount
= symbolCount
;
147 fSymbolTableEntrySize
= symbolTableEntrySize
;
148 fStringTableSize
= stringTableSize
;
154 // pragma mark - CoreFileThreadInfo
157 CoreFileThreadInfo::CoreFileThreadInfo(int32 id
, int32 state
, int32 priority
,
158 uint64 stackBase
, uint64 stackEnd
, const BString
& name
)
163 fStackBase(stackBase
),
172 CoreFileThreadInfo::~CoreFileThreadInfo()
179 CoreFileThreadInfo::SetCpuState(const void* state
, size_t size
)
186 fCpuState
= malloc(size
);
187 if (fCpuState
== NULL
)
189 memcpy(fCpuState
, state
, size
);
190 fCpuStateSize
= size
;
197 // pragma mark - CoreFile
204 fAreaInfos(32, true),
205 fImageInfos(32, true),
206 fThreadInfos(32, true)
211 CoreFile::~CoreFile()
217 CoreFile::Init(const char* fileName
)
219 status_t error
= fElfFile
.Init(fileName
);
223 if (fElfFile
.Is64Bit())
224 return _Init
<ElfClass64
>();
225 return _Init
<ElfClass32
>();
229 const CoreFileThreadInfo
*
230 CoreFile::ThreadInfoForId(int32 id
) const
232 int32 count
= fThreadInfos
.CountItems();
233 for (int32 i
= 0; i
< count
; i
++) {
234 CoreFileThreadInfo
* info
= fThreadInfos
.ItemAt(i
);
235 if (info
->Id() == id
)
244 CoreFile::CreateSymbolLookup(const CoreFileImageInfo
* imageInfo
,
245 ElfSymbolLookup
*& _lookup
)
247 // get the needed data
248 uint64 textDelta
= imageInfo
->TextDelta();
249 uint64 symbolTable
= imageInfo
->SymbolTable();
250 uint64 symbolHash
= imageInfo
->SymbolHash();
251 uint64 stringTable
= imageInfo
->StringTable();
252 CoreFileAreaInfo
* textArea
= imageInfo
->TextArea();
253 ElfSegment
* textSegment
= textArea
!= NULL
? textArea
->Segment() : NULL
;
255 if (symbolTable
== 0 || symbolHash
== 0 || stringTable
== 0
256 || textSegment
== NULL
) {
257 return B_UNSUPPORTED
;
260 // create a data source for the text segment
261 ElfSymbolLookupSource
* source
= fElfFile
.CreateSymbolLookupSource(
262 textSegment
->FileOffset(), textSegment
->FileSize(),
263 textSegment
->LoadAddress());
267 // get the symbol table entry size
268 // TODO: This is not actually correct, since at least theoretically the
269 // entry size may differ (cf. DT_SYMENT in the dynamic segment).
270 size_t symbolTableEntrySize
= fElfFile
.Is64Bit()
271 ? sizeof(ElfClass64::Sym
) : sizeof(ElfClass32::Sym
);
273 // create the symbol lookup
274 return ElfSymbolLookup::Create(source
, symbolTable
, symbolHash
, stringTable
,
275 ElfSymbolLookup::kGetSymbolCountFromHash
, symbolTableEntrySize
,
276 textDelta
, fElfFile
.Is64Bit(), fElfFile
.IsByteOrderSwapped(), true,
281 template<typename ElfClass
>
285 status_t error
= _ReadNotes
<ElfClass
>();
288 printf("CoreFile::_Init(): got %" B_PRId32
" areas, %" B_PRId32
" images, %"
289 B_PRId32
" threads\n", CountAreaInfos(), CountImageInfos(), CountThreadInfos());
290 // TODO: Verify that we actually read something!
295 template<typename ElfClass
>
297 CoreFile::_ReadNotes()
299 int32 count
= fElfFile
.CountSegments();
300 for (int32 i
= 0; i
< count
; i
++) {
301 ElfSegment
* segment
= fElfFile
.SegmentAt(i
);
302 if (segment
->Type() == PT_NOTE
) {
303 status_t error
= _ReadNotes
<ElfClass
>(segment
);
313 template<typename ElfClass
>
315 CoreFile::_ReadNotes(ElfSegment
* segment
)
317 // read the whole segment into memory
318 if ((uint64
)segment
->FileSize() > kMaxNotesSize
) {
319 WARNING("Notes segment too large (%" B_PRIdOFF
")\n",
320 segment
->FileSize());
321 return B_UNSUPPORTED
;
324 size_t notesSize
= (size_t)segment
->FileSize();
325 uint8
* notes
= (uint8
*)malloc(notesSize
);
328 MemoryDeleter
notesDeleter(notes
);
330 ssize_t bytesRead
= pread(fElfFile
.FD(), notes
, notesSize
,
331 (off_t
)segment
->FileOffset());
333 WARNING("Failed to read notes segment: %s\n", strerror(errno
));
336 if ((size_t)bytesRead
!= notesSize
) {
337 WARNING("Failed to read whole notes segment\n");
341 // iterate through notes
342 typedef typename
ElfClass::Nhdr Nhdr
;
343 while (notesSize
> 0) {
344 if (notesSize
< sizeof(Nhdr
)) {
345 WARNING("Remaining bytes in notes segment too short for header\n");
349 const Nhdr
* header
= (const Nhdr
*)notes
;
350 uint32 nameSize
= Get(header
->n_namesz
);
351 uint32 dataSize
= Get(header
->n_descsz
);
352 uint32 type
= Get(header
->n_type
);
354 notes
+= sizeof(Nhdr
);
355 notesSize
-= sizeof(Nhdr
);
357 size_t alignedNameSize
= (nameSize
+ 3) / 4 * 4;
358 if (alignedNameSize
> notesSize
) {
359 WARNING("Not enough bytes remaining in notes segment for note "
360 "name (%zu / %zu)\n", notesSize
, alignedNameSize
);
364 const char* name
= (const char*)notes
;
365 size_t nameLen
= strnlen(name
, nameSize
);
366 if (nameLen
== nameSize
) {
367 WARNING("Unterminated note name\n");
371 notes
+= alignedNameSize
;
372 notesSize
-= alignedNameSize
;
374 size_t alignedDataSize
= (dataSize
+ 3) / 4 * 4;
375 if (alignedDataSize
> notesSize
) {
376 WARNING("Not enough bytes remaining in notes segment for note "
381 _ReadNote
<ElfClass
>(name
, type
, notes
, dataSize
);
383 notes
+= alignedDataSize
;
384 notesSize
-= alignedDataSize
;
391 template<typename ElfClass
>
393 CoreFile::_ReadNote(const char* name
, uint32 type
, const void* data
,
396 if (strcmp(name
, ELF_NOTE_CORE
) == 0) {
402 } else if (strcmp(name
, ELF_NOTE_HAIKU
) == 0) {
405 return _ReadTeamNote
<ElfClass
>(data
, dataSize
);
407 return _ReadAreasNote
<ElfClass
>(data
, dataSize
);
409 return _ReadImagesNote
<ElfClass
>(data
, dataSize
);
411 return _ReadSymbolsNote
<ElfClass
>(data
, dataSize
);
413 return _ReadThreadsNote
<ElfClass
>(data
, dataSize
);
418 WARNING("Unsupported note type %s/%#" B_PRIx32
"\n", name
, type
);
423 template<typename ElfClass
>
425 CoreFile::_ReadTeamNote(const void* data
, uint32 dataSize
)
427 typedef typename
ElfClass::NoteTeam NoteTeam
;
429 if (dataSize
< sizeof(uint32
)) {
430 WARNING("Team note too short\n");
433 uint32 entrySize
= Get(*(const uint32
*)data
);
434 data
= (const uint32
*)data
+ 1;
435 dataSize
-= sizeof(uint32
);
437 if (entrySize
== 0 || dataSize
== 0 || dataSize
- 1 < entrySize
) {
438 WARNING("Team note: too short or invalid entry size (%" B_PRIu32
")\n",
444 _ReadEntry(data
, dataSize
, note
, entrySize
);
446 // check, if args are null-terminated
447 const char* args
= (const char*)data
;
448 size_t argsSize
= dataSize
;
449 if (args
[argsSize
- 1] != '\0') {
450 WARNING("Team note args not terminated\n");
454 int32 id
= Get(note
.nt_id
);
455 int32 uid
= Get(note
.nt_uid
);
456 int32 gid
= Get(note
.nt_gid
);
458 BString
copiedArgs(args
);
459 if (args
[0] != '\0' && copiedArgs
.Length() == 0)
462 fTeamInfo
.Init(id
, uid
, gid
, copiedArgs
);
467 template<typename ElfClass
>
469 CoreFile::_ReadAreasNote(const void* data
, uint32 dataSize
)
471 if (dataSize
< 2 * sizeof(uint32
)) {
472 WARNING("Areas note too short\n");
475 uint32 areaCount
= _ReadValue
<uint32
>(data
, dataSize
);
476 uint32 entrySize
= _ReadValue
<uint32
>(data
, dataSize
);
478 typedef typename
ElfClass::NoteAreaEntry Entry
;
483 // check entry size and area count
484 if (entrySize
== 0 || dataSize
== 0 || areaCount
> dataSize
485 || dataSize
- 1 < entrySize
|| areaCount
* entrySize
>= dataSize
) {
486 WARNING("Areas note: too short or invalid entry size (%" B_PRIu32
")\n",
491 // check, if strings are null-terminated
492 const char* strings
= (const char*)data
+ areaCount
* entrySize
;
493 size_t stringsSize
= dataSize
- areaCount
* entrySize
;
494 if (stringsSize
== 0 || strings
[stringsSize
- 1] != '\0') {
495 WARNING("Areas note strings not terminated\n");
499 for (uint64 i
= 0; i
< areaCount
; i
++) {
502 _ReadEntry(data
, dataSize
, entry
, entrySize
);
504 int32 id
= Get(entry
.na_id
);
505 uint64 baseAddress
= Get(entry
.na_base
);
506 uint64 size
= Get(entry
.na_size
);
507 uint64 ramSize
= Get(entry
.na_ram_size
);
508 uint32 lock
= Get(entry
.na_lock
);
509 uint32 protection
= Get(entry
.na_protection
);
512 if (stringsSize
== 0) {
513 WARNING("Area %" B_PRIu64
" (ID %#" B_PRIx32
" @ %#" B_PRIx64
514 ") has no name\n", i
, id
, baseAddress
);
517 const char* name
= strings
;
518 size_t nameSize
= strlen(name
) + 1;
520 stringsSize
-= nameSize
;
522 BString
copiedName(name
);
523 if (name
[0] != '\0' && copiedName
.Length() == 0)
526 // create and add area
527 ElfSegment
* segment
= _FindAreaSegment(baseAddress
);
528 if (segment
== NULL
) {
529 WARNING("No matching segment found for area %" B_PRIu64
" (ID %#"
530 B_PRIx32
" @ %#" B_PRIx64
", name: '%s')", i
, id
, baseAddress
,
535 CoreFileAreaInfo
* area
= new(std::nothrow
) CoreFileAreaInfo(segment
, id
,
536 baseAddress
, size
, ramSize
, lock
, protection
, copiedName
);
537 if (area
== NULL
|| !fAreaInfos
.AddItem(area
)) {
547 template<typename ElfClass
>
549 CoreFile::_ReadImagesNote(const void* data
, uint32 dataSize
)
551 if (dataSize
< 2 * sizeof(uint32
)) {
552 WARNING("Images note too short\n");
555 uint32 imageCount
= _ReadValue
<uint32
>(data
, dataSize
);
556 uint32 entrySize
= _ReadValue
<uint32
>(data
, dataSize
);
558 typedef typename
ElfClass::NoteImageEntry Entry
;
563 // check entry size and image count
564 if (entrySize
== 0 || dataSize
== 0 || imageCount
> dataSize
565 || dataSize
- 1 < entrySize
|| imageCount
* entrySize
>= dataSize
) {
566 WARNING("Images note: too short or invalid entry size (%" B_PRIu32
571 // check, if strings are null-terminated
572 const char* strings
= (const char*)data
+ imageCount
* entrySize
;
573 size_t stringsSize
= dataSize
- imageCount
* entrySize
;
574 if (stringsSize
== 0 || strings
[stringsSize
- 1] != '\0') {
575 WARNING("Images note strings not terminated\n");
579 for (uint64 i
= 0; i
< imageCount
; i
++) {
582 _ReadEntry(data
, dataSize
, entry
, entrySize
);
584 int32 id
= Get(entry
.ni_id
);
585 int32 type
= Get(entry
.ni_type
);
586 uint64 initRoutine
= Get(entry
.ni_init_routine
);
587 uint64 termRoutine
= Get(entry
.ni_term_routine
);
588 uint64 textBase
= Get(entry
.ni_text_base
);
589 uint64 textSize
= Get(entry
.ni_text_size
);
590 int64 textDelta
= Get(entry
.ni_text_delta
);
591 uint64 dataBase
= Get(entry
.ni_data_base
);
592 uint64 dataSize
= Get(entry
.ni_data_size
);
593 int32 deviceId
= Get(entry
.ni_device
);
594 int64 nodeId
= Get(entry
.ni_node
);
595 uint64 symbolTable
= Get(entry
.ni_symbol_table
);
596 uint64 symbolHash
= Get(entry
.ni_symbol_hash
);
597 uint64 stringTable
= Get(entry
.ni_string_table
);
600 if (stringsSize
== 0) {
601 WARNING("Image %" B_PRIu64
" (ID %#" B_PRIx32
") has no name\n",
605 const char* name
= strings
;
606 size_t nameSize
= strlen(name
) + 1;
608 stringsSize
-= nameSize
;
610 BString
copiedName(name
);
611 if (name
[0] != '\0' && copiedName
.Length() == 0)
614 // create and add image
615 CoreFileAreaInfo
* textArea
= _FindArea(textBase
);
616 CoreFileAreaInfo
* dataArea
= _FindArea(dataBase
);
617 CoreFileImageInfo
* image
= new(std::nothrow
) CoreFileImageInfo(id
, type
,
618 initRoutine
, termRoutine
, textBase
, textSize
, textDelta
, dataBase
,
619 dataSize
, deviceId
, nodeId
, symbolTable
, symbolHash
, stringTable
,
620 textArea
, dataArea
, copiedName
);
621 if (image
== NULL
|| !fImageInfos
.AddItem(image
)) {
631 template<typename ElfClass
>
633 CoreFile::_ReadSymbolsNote(const void* data
, uint32 dataSize
)
635 if (dataSize
< 3 * sizeof(uint32
)) {
636 WARNING("Symbols note too short\n");
639 int32 imageId
= _ReadValue
<int32
>(data
, dataSize
);
640 uint32 symbolCount
= _ReadValue
<uint32
>(data
, dataSize
);
641 uint32 entrySize
= _ReadValue
<uint32
>(data
, dataSize
);
643 typedef typename
ElfClass::Sym Sym
;
645 if (symbolCount
== 0)
648 // get the corresponding image
649 CoreFileImageInfo
* imageInfo
= _ImageInfoForId(imageId
);
650 if (imageInfo
== NULL
) {
651 WARNING("Symbols note: image (ID %" B_PRId32
") not found\n",
656 // check entry size and symbol count
657 if (entrySize
< sizeof(Sym
) || symbolCount
> dataSize
658 || dataSize
- 1 < entrySize
659 || symbolCount
* entrySize
>= dataSize
- 1) {
660 WARNING("Symbols note: too short or invalid entry size (%" B_PRIu32
665 uint32 symbolTableSize
= symbolCount
* entrySize
;
666 uint32 stringTableSize
= dataSize
- symbolTableSize
;
668 // check, if the string table is null-terminated
669 const char* stringTable
= (const char*)data
+ symbolTableSize
;
670 if (stringTableSize
== 0 || stringTable
[stringTableSize
- 1] != '\0') {
671 WARNING("Symbols note string table not terminated\n");
675 CoreFileSymbolsInfo
* symbolsInfo
= new(std::nothrow
) CoreFileSymbolsInfo
;
676 if (symbolsInfo
== NULL
677 || !symbolsInfo
->Init(data
, symbolCount
, entrySize
, stringTable
,
683 imageInfo
->SetSymbolsInfo(symbolsInfo
);
689 template<typename ElfClass
>
691 CoreFile::_ReadThreadsNote(const void* data
, uint32 dataSize
)
693 if (dataSize
< 3 * sizeof(uint32
)) {
694 WARNING("Threads note too short\n");
697 uint32 threadCount
= _ReadValue
<uint32
>(data
, dataSize
);
698 uint32 entrySize
= _ReadValue
<uint32
>(data
, dataSize
);
699 uint32 cpuStateSize
= _ReadValue
<uint32
>(data
, dataSize
);
701 if (cpuStateSize
> 1024 * 1024) {
702 WARNING("Threads note: unreasonable CPU state size: %" B_PRIu32
"\n",
707 typedef typename
ElfClass::NoteThreadEntry Entry
;
709 if (threadCount
== 0)
712 size_t totalEntrySize
= entrySize
+ cpuStateSize
;
714 // check entry size and thread count
715 if (entrySize
== 0 || dataSize
== 0 || threadCount
> dataSize
716 || entrySize
> dataSize
|| cpuStateSize
> dataSize
717 || dataSize
- 1 < totalEntrySize
718 || threadCount
* totalEntrySize
>= dataSize
) {
719 WARNING("Threads note: too short or invalid entry size (%" B_PRIu32
724 // check, if strings are null-terminated
725 const char* strings
= (const char*)data
+ threadCount
* totalEntrySize
;
726 size_t stringsSize
= dataSize
- threadCount
* totalEntrySize
;
727 if (stringsSize
== 0 || strings
[stringsSize
- 1] != '\0') {
728 WARNING("Threads note strings not terminated\n");
732 for (uint64 i
= 0; i
< threadCount
; i
++) {
735 _ReadEntry(data
, dataSize
, entry
, entrySize
);
737 int32 id
= Get(entry
.nth_id
);
738 int32 state
= Get(entry
.nth_state
);
739 int32 priority
= Get(entry
.nth_priority
);
740 uint64 stackBase
= Get(entry
.nth_stack_base
);
741 uint64 stackEnd
= Get(entry
.nth_stack_end
);
744 if (stringsSize
== 0) {
745 WARNING("Thread %" B_PRIu64
" (ID %#" B_PRIx32
") has no name\n",
749 const char* name
= strings
;
750 size_t nameSize
= strlen(name
) + 1;
752 stringsSize
-= nameSize
;
754 BString
copiedName(name
);
755 if (name
[0] != '\0' && copiedName
.Length() == 0)
758 // create and add thread
759 CoreFileThreadInfo
* thread
= new(std::nothrow
) CoreFileThreadInfo(id
,
760 state
, priority
, stackBase
, stackEnd
, copiedName
);
761 if (thread
== NULL
|| !fThreadInfos
.AddItem(thread
)) {
767 if (!thread
->SetCpuState(data
, cpuStateSize
))
769 _Advance(data
, dataSize
, cpuStateSize
);
777 CoreFile::_FindArea(uint64 address
) const
779 int32 count
= fAreaInfos
.CountItems();
780 for (int32 i
= 0; i
< count
; i
++) {
781 CoreFileAreaInfo
* area
= fAreaInfos
.ItemAt(i
);
782 if (address
>= area
->BaseAddress()
783 && address
< area
->EndAddress()) {
793 CoreFile::_FindAreaSegment(uint64 address
) const
795 int32 count
= fElfFile
.CountSegments();
796 for (int32 i
= 0; i
< count
; i
++) {
797 ElfSegment
* segment
= fElfFile
.SegmentAt(i
);
798 if (segment
->Type() == PT_LOAD
&& segment
->LoadAddress() == address
)
807 CoreFile::_ImageInfoForId(int32 id
) const
809 int32 count
= fImageInfos
.CountItems();
810 for (int32 i
= 0; i
< count
; i
++) {
811 CoreFileImageInfo
* info
= fImageInfos
.ItemAt(i
);
812 if (info
->Id() == id
)
820 template<typename Type
>
822 CoreFile::_ReadValue(const void*& data
, uint32
& dataSize
)
824 Type value
= Get(*(const Type
*)data
);
825 _Advance(data
, dataSize
, sizeof(Type
));
830 template<typename Entry
>
832 CoreFile::_ReadEntry(const void*& data
, uint32
& dataSize
, Entry
& entry
,
835 memcpy(&entry
, data
, std::min(sizeof(entry
), entrySize
));
836 _Advance(data
, dataSize
, entrySize
);
841 CoreFile::_Advance(const void*& data
, uint32
& dataSize
, size_t by
)
843 data
= (const uint8
*)data
+ by
;