1 // Copyright 2011 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 // * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 // elf_core_dump.cc: Implement google_breakpad::ElfCoreDump.
30 // See elf_core_dump.h for details.
31
32 #ifdef HAVE_CONFIG_H
33 #include <config.h> // Must come first
34 #endif
35
36 #include "common/linux/elf_core_dump.h"
37
38 #include <stddef.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 namespace google_breakpad {
43
44 // Implementation of ElfCoreDump::Note.
45
Note()46 ElfCoreDump::Note::Note() {}
47
Note(const MemoryRange & content)48 ElfCoreDump::Note::Note(const MemoryRange& content) : content_(content) {}
49
IsValid() const50 bool ElfCoreDump::Note::IsValid() const {
51 return GetHeader() != NULL;
52 }
53
GetHeader() const54 const ElfCoreDump::Nhdr* ElfCoreDump::Note::GetHeader() const {
55 return content_.GetData<Nhdr>(0);
56 }
57
GetType() const58 ElfCoreDump::Word ElfCoreDump::Note::GetType() const {
59 const Nhdr* header = GetHeader();
60 // 0 is not being used as a NOTE type.
61 return header ? header->n_type : 0;
62 }
63
GetName() const64 MemoryRange ElfCoreDump::Note::GetName() const {
65 const Nhdr* header = GetHeader();
66 if (header) {
67 return content_.Subrange(sizeof(Nhdr), header->n_namesz);
68 }
69 return MemoryRange();
70 }
71
GetDescription() const72 MemoryRange ElfCoreDump::Note::GetDescription() const {
73 const Nhdr* header = GetHeader();
74 if (header) {
75 return content_.Subrange(AlignedSize(sizeof(Nhdr) + header->n_namesz),
76 header->n_descsz);
77 }
78 return MemoryRange();
79 }
80
GetNextNote() const81 ElfCoreDump::Note ElfCoreDump::Note::GetNextNote() const {
82 MemoryRange next_content;
83 const Nhdr* header = GetHeader();
84 if (header) {
85 size_t next_offset = AlignedSize(sizeof(Nhdr) + header->n_namesz);
86 next_offset = AlignedSize(next_offset + header->n_descsz);
87 next_content =
88 content_.Subrange(next_offset, content_.length() - next_offset);
89 }
90 return Note(next_content);
91 }
92
93 // static
AlignedSize(size_t size)94 size_t ElfCoreDump::Note::AlignedSize(size_t size) {
95 size_t mask = sizeof(Word) - 1;
96 return (size + mask) & ~mask;
97 }
98
99
100 // Implementation of ElfCoreDump.
101
ElfCoreDump()102 ElfCoreDump::ElfCoreDump() : proc_mem_fd_(-1) {}
103
ElfCoreDump(const MemoryRange & content)104 ElfCoreDump::ElfCoreDump(const MemoryRange& content)
105 : content_(content), proc_mem_fd_(-1) {}
106
~ElfCoreDump()107 ElfCoreDump::~ElfCoreDump() {
108 if (proc_mem_fd_ != -1) {
109 close(proc_mem_fd_);
110 proc_mem_fd_ = -1;
111 }
112 }
113
SetContent(const MemoryRange & content)114 void ElfCoreDump::SetContent(const MemoryRange& content) {
115 content_ = content;
116 }
117
SetProcMem(int fd)118 void ElfCoreDump::SetProcMem(int fd) {
119 if (proc_mem_fd_ != -1) {
120 close(proc_mem_fd_);
121 }
122 proc_mem_fd_ = fd;
123 }
124
IsValid() const125 bool ElfCoreDump::IsValid() const {
126 const Ehdr* header = GetHeader();
127 return (header &&
128 header->e_ident[0] == ELFMAG0 &&
129 header->e_ident[1] == ELFMAG1 &&
130 header->e_ident[2] == ELFMAG2 &&
131 header->e_ident[3] == ELFMAG3 &&
132 header->e_ident[4] == kClass &&
133 header->e_version == EV_CURRENT &&
134 header->e_type == ET_CORE);
135 }
136
GetHeader() const137 const ElfCoreDump::Ehdr* ElfCoreDump::GetHeader() const {
138 return content_.GetData<Ehdr>(0);
139 }
140
GetProgramHeader(unsigned index) const141 const ElfCoreDump::Phdr* ElfCoreDump::GetProgramHeader(unsigned index) const {
142 const Ehdr* header = GetHeader();
143 if (header) {
144 return reinterpret_cast<const Phdr*>(content_.GetArrayElement(
145 header->e_phoff, header->e_phentsize, index));
146 }
147 return NULL;
148 }
149
GetFirstProgramHeaderOfType(Word type) const150 const ElfCoreDump::Phdr* ElfCoreDump::GetFirstProgramHeaderOfType(
151 Word type) const {
152 for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) {
153 const Phdr* program = GetProgramHeader(i);
154 if (program->p_type == type) {
155 return program;
156 }
157 }
158 return NULL;
159 }
160
GetProgramHeaderCount() const161 unsigned ElfCoreDump::GetProgramHeaderCount() const {
162 const Ehdr* header = GetHeader();
163 return header ? header->e_phnum : 0;
164 }
165
CopyData(void * buffer,Addr virtual_address,size_t length)166 bool ElfCoreDump::CopyData(void* buffer, Addr virtual_address, size_t length) {
167 for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) {
168 const Phdr* program = GetProgramHeader(i);
169 if (program->p_type != PT_LOAD)
170 continue;
171
172 size_t offset_in_segment = virtual_address - program->p_vaddr;
173 if (virtual_address >= program->p_vaddr &&
174 offset_in_segment < program->p_filesz) {
175 const void* data =
176 content_.GetData(program->p_offset + offset_in_segment, length);
177 if (data) {
178 memcpy(buffer, data, length);
179 return true;
180 }
181 }
182 }
183
184 /* fallback: if available, read from /proc/<pid>/mem */
185 if (proc_mem_fd_ != -1) {
186 off_t offset = virtual_address;
187 ssize_t r = pread(proc_mem_fd_, buffer, length, offset);
188 if (r < ssize_t(length)) {
189 return false;
190 }
191 return true;
192 }
193 return false;
194 }
195
GetFirstNote() const196 ElfCoreDump::Note ElfCoreDump::GetFirstNote() const {
197 MemoryRange note_content;
198 const Phdr* program_header = GetFirstProgramHeaderOfType(PT_NOTE);
199 if (program_header) {
200 note_content = content_.Subrange(program_header->p_offset,
201 program_header->p_filesz);
202 }
203 return Note(note_content);
204 }
205
206 } // namespace google_breakpad
207