summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibCoredump/Reader.h
blob: 8f13f74f7012974205ea137c849d227834e8ac91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
 * Copyright (c) 2022, the SerenityOS developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/ByteReader.h>
#include <AK/HashMap.h>
#include <AK/Noncopyable.h>
#include <AK/OwnPtr.h>
#include <LibCore/MappedFile.h>
#include <LibELF/Core.h>
#include <LibELF/Image.h>

namespace Coredump {

struct MemoryRegionInfo {
    ELF::Core::NotesEntryHeader header;
    uint64_t region_start;
    uint64_t region_end;
    uint16_t program_header_index;
    StringView region_name;

    StringView object_name() const
    {
        if (region_name.contains("Loader.so"))
            return "Loader.so"sv;
        auto maybe_colon_index = region_name.find(':');
        if (!maybe_colon_index.has_value())
            return {};
        return region_name.substring_view(0, *maybe_colon_index);
    }
};

class Reader {
    AK_MAKE_NONCOPYABLE(Reader);
    AK_MAKE_NONMOVABLE(Reader);

public:
    static OwnPtr<Reader> create(StringView);
    ~Reader() = default;

    template<typename Func>
    void for_each_memory_region_info(Func func) const;

    struct LibraryInfo {
        String name;
        String path;
        FlatPtr base_address { 0 };
    };

    void for_each_library(Function<void(LibraryInfo)> func) const;

    template<typename Func>
    void for_each_thread_info(Func func) const;

    const ELF::Image& image() const { return m_coredump_image; }

    Optional<FlatPtr> peek_memory(FlatPtr address) const;
    Optional<MemoryRegionInfo> first_region_for_object(StringView object_name) const;
    Optional<MemoryRegionInfo> region_containing(FlatPtr address) const;

    struct LibraryData {
        String name;
        FlatPtr base_address { 0 };
        NonnullRefPtr<Core::MappedFile> file;
        ELF::Image lib_elf;
    };
    LibraryData const* library_containing(FlatPtr address) const;

    String resolve_object_path(StringView object_name) const;

    int process_pid() const;
    u8 process_termination_signal() const;
    String process_executable_path() const;
    Vector<String> process_arguments() const;
    Vector<String> process_environment() const;
    HashMap<String, String> metadata() const;

private:
    explicit Reader(ReadonlyBytes);
    explicit Reader(ByteBuffer);
    explicit Reader(NonnullRefPtr<Core::MappedFile>);

    static Optional<ByteBuffer> decompress_coredump(ReadonlyBytes);

    class NotesEntryIterator {
    public:
        NotesEntryIterator(u8 const* notes_data);

        ELF::Core::NotesEntryHeader::Type type() const;
        const ELF::Core::NotesEntry* current() const;

        void next();
        bool at_end() const;

    private:
        const ELF::Core::NotesEntry* m_current { nullptr };
        u8 const* start { nullptr };
    };

    // Private as we don't need anyone poking around in this JsonObject
    // manually - we know very well what should be included and expose that
    // as getters with the appropriate (non-JsonValue) types.
    const JsonObject process_info() const;

    // For uncompressed coredumps, we keep the MappedFile
    RefPtr<Core::MappedFile> m_mapped_file;

    // For compressed coredumps, we decompress them into a ByteBuffer
    ByteBuffer m_coredump_buffer;

    ReadonlyBytes m_coredump_bytes;

    ELF::Image m_coredump_image;
    ssize_t m_notes_segment_index { -1 };
};

template<typename Func>
void Reader::for_each_memory_region_info(Func func) const
{
    NotesEntryIterator it(bit_cast<u8 const*>(m_coredump_image.program_header(m_notes_segment_index).raw_data()));
    for (; !it.at_end(); it.next()) {
        if (it.type() != ELF::Core::NotesEntryHeader::Type::MemoryRegionInfo)
            continue;
        ELF::Core::MemoryRegionInfo raw_memory_region_info;
        ReadonlyBytes raw_data {
            it.current(),
            sizeof(raw_memory_region_info),
        };
        ByteReader::load(raw_data.data(), raw_memory_region_info);

        MemoryRegionInfo memory_region_info {
            raw_memory_region_info.header,
            raw_memory_region_info.region_start,
            raw_memory_region_info.region_end,
            raw_memory_region_info.program_header_index,
            { bit_cast<char const*>(raw_data.offset_pointer(raw_data.size())) },
        };
        IterationDecision decision = func(memory_region_info);
        if (decision == IterationDecision::Break)
            return;
    }
}

template<typename Func>
void Reader::for_each_thread_info(Func func) const
{
    NotesEntryIterator it(bit_cast<u8 const*>(m_coredump_image.program_header(m_notes_segment_index).raw_data()));
    for (; !it.at_end(); it.next()) {
        if (it.type() != ELF::Core::NotesEntryHeader::Type::ThreadInfo)
            continue;
        ELF::Core::ThreadInfo thread_info;
        ByteReader::load(bit_cast<u8 const*>(it.current()), thread_info);

        IterationDecision decision = func(thread_info);
        if (decision == IterationDecision::Break)
            return;
    }
}

}