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