* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
#include "program.hpp"
#include <fcntl.h>
+#include <gelf.h>
#include <libelf.h>
#include <limits.h>
+#include <link.h>
#include <sys/procfs.h>
+extern "C" {
+#include <thread_db.h>
+}
+
#include <cstring>
#include <iostream>
+#include <map>
#include <memory>
#include <string>
#include <vector>
+static constexpr size_t PAGESIZE = 0x1000;
template <typename T>
class Core {
private:
- static constexpr size_t PAGESIZE = 0x1000;
ProgramTableEntryBase<T> *m_note_header;
std::vector<std::unique_ptr<ProgramTableEntryBase<T>>> m_phdrt;
+ std::vector<std::unique_ptr<SymData>> m_symdata;
size_t current_data_address;
std::vector <std::unique_ptr<Note>> m_notes;
typename T::Ehdr m_ehdr;
public:
Core() : m_note_header(new ProgramTableEntryNote<T>()),
- current_data_address(sizeof(m_ehdr)) {
- m_phdrt.push_back(std::unique_ptr<ProgramTableEntryBase<T>>(m_note_header));
+ current_data_address(sizeof(m_ehdr)),
+ m_ehdr() {
+ m_phdrt.push_back(std::unique_ptr<ProgramTableEntryBase<T>>(m_note_header));
}
void FillELFHeader() {
entry->header.p_filesz = record->end - record->start;
entry->header.p_memsz = record->end - record->start;
entry->header.p_align = PAGESIZE;
+ entry->important = record->important;
m_phdrt.push_back(std::unique_ptr<ProgramTableEntryLoad<T>>(entry));
}
}
}
- void SaveLoadable(const std::string &mem_path, std::ofstream &core_file) {
- int fd = open(mem_path.c_str(), O_RDONLY);
- if (fd == -1)
- throw std::system_error(errno, std::system_category(), "failed to open mem file " + mem_path);
-
+ void SaveLoadable(int mem_fd, std::ofstream &core_file, bool minicore) {
for (const auto &record : m_phdrt) {
if (record->header.p_type != PT_LOAD)
continue;
- if (lseek64(fd, record->header.p_vaddr, SEEK_SET) == -1)
+ if (lseek64(mem_fd, record->header.p_vaddr, SEEK_SET) == -1)
logger.log_error("lseek64 error: %s", std::system_category().default_error_condition(errno).message());
- CopyData(fd, core_file, record->header.p_filesz);
+ if (!minicore || static_cast<ProgramTableEntryLoad<T>*>(record.get())->important)
+ CopyData(mem_fd, core_file, record->header.p_filesz);
+ else
+ core_file.seekp(record->header.p_filesz, std::ios_base::cur);
}
- close(fd);
}
void AddNote(Note *note) {
for (const auto &record : records)
ImportMapRecord(record);
}
-};
-#endif // CORE_HPP__
+ typename T::uint AUXVval(const typename T::auxv_t *auxv, typename T::Addr type) {
+ for (int i = 0; auxv[i].a_type != AT_NULL; i++) {
+ if (auxv[i].a_type == type)
+ return auxv[i].a_un.a_val;
+ }
+ return 0;
+ }
+
+ void ReadFromFile(std::ifstream &input, unsigned long address, void *data, size_t len) {
+ input.seekg(address, std::ios_base::beg);
+ input.read(static_cast<char*>(data), len);
+ }
+
+ void ReadFromFile(const int fd, unsigned long address, void *data, size_t len) {
+ lseek64(fd, address, SEEK_SET);
+ if (read(fd, data, len) == -1)
+ throw std::system_error(errno, std::system_category(), "failed to read at " + std::to_string(address));
+ }
+
+ void DumpData(int fd, std::ofstream &output, typename T::Addr iaddress, typename T::Addr oaddress, size_t len, const std::string &desc) {
+ lseek64(fd, iaddress, SEEK_SET);
+ output.seekp(oaddress, std::ios_base::beg);
+ logger.log_info("dumping %s: 0x%x-0x%x to 0x%x (%d bytes)", desc.c_str(), iaddress, iaddress+len, oaddress, len);
+ CopyData(fd, output, len);
+ }
+
+ void DumpData(int fd, std::ofstream &output, typename T::Addr iaddress, size_t len, const std::string &desc) {
+ typename T::Addr out_addr = MemAddr2FileAddr(iaddress);
+ if (out_addr)
+ DumpData(fd, output, iaddress, out_addr, len, desc);
+ else
+ logger.log_error("No address for 0x%x", iaddress);
+ }
+
+ ProgramTableEntryLoad<T>* FindRecordForMemAddress(typename T::Addr address) {
+ for (auto &record : m_phdrt) {
+ if (record->header.p_type != PT_LOAD)
+ continue;
+
+ ProgramTableEntryLoad<T>* entry = static_cast<ProgramTableEntryLoad<T>*>(record.get());
+ if (address >= entry->header.p_vaddr && address < entry->header.p_vaddr + entry->header.p_memsz) {
+ return entry;
+ }
+ }
+ return nullptr;
+ }
+
+ typename T::Addr MemAddr2FileAddr(typename T::Addr address) {
+ ProgramTableEntryLoad<T> *record = FindRecordForMemAddress(address);
+
+ if (record == nullptr)
+ return 0;
+
+ return record->header.p_offset + (address - record->header.p_vaddr);
+ }
+
+ SymData* AllocSymData(const std::string &lib, unsigned long start, typename T::Word type) {
+ elf_version(EV_CURRENT);
+ auto sym_data = new SymData();
+ Elf_Scn *scn = nullptr;
+
+ sym_data->start = start;
+ sym_data->fd = open(lib.c_str(), O_RDONLY);
+
+ if (sym_data->fd < 0) {
+ delete sym_data;
+ return nullptr;
+ }
+
+ sym_data->elf = elf_begin(sym_data->fd, ELF_C_READ, nullptr);
+
+ while (1) {
+ GElf_Shdr *shdr;
+ scn = elf_nextscn(sym_data->elf, scn);
+
+ if (!scn) {
+ elf_end(sym_data->elf);
+ close(sym_data->fd);
+ delete sym_data;
+ logger.log_info("no sym data for %s", lib.c_str());
+ return nullptr;
+ }
+
+ shdr = gelf_getshdr(scn, &sym_data->shdr);
+ if (shdr && sym_data->shdr.sh_type == type)
+ break;
+ }
+
+ sym_data->data = elf_getdata(scn, nullptr);
+ sym_data->count = sym_data->shdr.sh_size / sym_data->shdr.sh_entsize;
+
+ return sym_data;
+ }
+
+ void SaveSymData(const std::string &lib, unsigned long start) {
+ SymData* sym_data;
+
+ for (auto type: {SHT_SYMTAB, SHT_DYNSYM}) {
+ sym_data = AllocSymData(lib, start, type);
+ if (!sym_data)
+ continue;
+
+ m_symdata.push_back(std::unique_ptr<SymData>(sym_data));
+ }
+ }
+
+ static int FindPthreadsCb(const td_thrhandle_t *th, void *cb_data) {
+ td_thrinfo_t thinfo;
+ td_thr_get_info(th, &thinfo);
+ return TD_OK;
+ }
+
+ void SavePthreadList(int mem_fd, std::ofstream &core_file, int pid) {
+ struct ps_prochandle ph = {
+ .core_obj = this,
+ .bit32 = (std::is_same<T, Elf32>::value),
+ .mem_fd = mem_fd,
+ .core_file = core_file,
+ .pid = pid
+ };
+
+ td_thragent_t *ta;
+ td_err_e err;
+
+ err = td_ta_new(&ph, &ta);
+ if (err == TD_OK) {
+ err = td_ta_thr_iter(ta, FindPthreadsCb, nullptr, TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+ TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+ td_ta_delete(ta);
+ }
+
+ if (err == TD_NOLIBTHREAD) {
+ logger.log_info("target does not appear to be multi-threaded");
+ } else if (err != TD_OK) {
+ logger.log_info("WARNING: libthread_db not found, using fallback");
+ // TODO: fallback
+ }
+ }
+
+ bool SymAddress(const char *sym_name, unsigned long *addr) {
+ for (const auto &sd : m_symdata) {
+ for (int i = 0; i < sd->count; i++) {
+ GElf_Sym sym;
+ GElf_Sym *s;
+
+ s = gelf_getsym(sd->data, i, &sym);
+ if (!s)
+ continue;
+
+ const char *st = elf_strptr(sd->elf, sd->shdr.sh_link, s->st_name);
+
+ if (strcmp(st, sym_name) != 0)
+ continue;
+
+ *addr = sd->start + s->st_value;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void SaveAUXVData(const std::string &exe_path, int mem_fd, std::ofstream &core_file) {
+ unsigned long debug_ptr = 0;
+ NoteCoreAUXV *auxv_note = nullptr;
+
+ for (auto ¬e : m_notes) {
+ if (note->type == PT_PHDR) {
+ auxv_note = static_cast<NoteCoreAUXV*>(note.get());
+ break;
+ }
+ }
+ if (!auxv_note)
+ return;
+
+ auto *auxv_data = reinterpret_cast<const typename T::auxv_t*>(auxv_note->auxv().data());
+
+ auto phnum = AUXVval(auxv_data, AT_PHNUM);
+ auto phdr = AUXVval(auxv_data, AT_PHDR);
+
+ if (phdr == 0) {
+ logger.log_info("PHDR not found for %s", exe_path);
+ return;
+ }
+
+ typename T::Addr relocation = 0, dyn_addr = 0;
+
+ size_t i;
+
+ for (i = 0; i < phnum; i++) {
+ auto addr = phdr + (sizeof(typename T::Phdr) * i) + offsetof(typename T::Phdr, p_type);
+ uint32_t val;
+ ReadFromFile(mem_fd, addr, &val, sizeof(val));
+
+ if (val == PT_NULL) {
+ break;
+ } else if (val == PT_PHDR) {
+ addr = phdr + (sizeof(typename T::Phdr) * i) + offsetof(typename T::Phdr, p_vaddr);
+ ReadFromFile(mem_fd, addr, &relocation, sizeof(relocation));
+ if (relocation > phdr) {
+ logger.log_error("relocation is greather than phdr");
+ return;
+ }
+ relocation = phdr - relocation;
+ } else if (val == PT_DYNAMIC) {
+ addr = phdr + (sizeof(typename T::Phdr) * i) + offsetof(typename T::Phdr, p_vaddr);
+ ReadFromFile(mem_fd, addr, &dyn_addr, sizeof(dyn_addr));
+ }
+ }
+
+ DumpData(mem_fd, core_file, phdr, sizeof(typename T::Phdr)*i, "phdr");
+
+ SaveSymData(exe_path, relocation);
+
+ dyn_addr += relocation;
+
+
+ for (i = 0; ; i++) {
+ unsigned long addr = dyn_addr + (sizeof(typename T::Dyn)*i) + offsetof(typename T::Dyn, d_tag);
+ uint32_t val;
+ ReadFromFile(mem_fd, addr, &val, sizeof(val));
+ if (val == DT_NULL)
+ break;
+ if (val == DT_DEBUG) {
+ addr = dyn_addr + sizeof(typename T::Dyn)*i + offsetof(typename T::Dyn, d_un.d_ptr);
+ ReadFromFile(mem_fd, addr, &debug_ptr, sizeof(debug_ptr));
+ }
+ }
+ if (!debug_ptr) {
+ logger.log_error("debug_ptr not found");
+ return;
+ }
+ DumpData(mem_fd, core_file, dyn_addr, sizeof(typename T::Dyn)*i, "auxv dyns");
+ DumpData(mem_fd, core_file, debug_ptr, sizeof(struct r_debug), "auxv r_debug");
+
+ unsigned long ptr = debug_ptr;
+ ReadFromFile(mem_fd, ptr + offsetof(struct r_debug, r_map), &ptr, sizeof(ptr));
+
+ while (ptr) {
+ unsigned long addr = 0;
+ DumpData(mem_fd, core_file, ptr, sizeof(struct link_map), "auxv link_map");
+
+ ReadFromFile(mem_fd, ptr + offsetof(struct link_map, l_name), &addr, sizeof(addr));
+
+ char buff[PAGESIZE];
+ ReadFromFile(mem_fd, addr, buff, sizeof(buff));
+
+ if (buff[0] != 0) {
+ DumpData(mem_fd, core_file, addr, strlen(buff) + 1, "l_name");
+ ReadFromFile(mem_fd, ptr+offsetof(struct link_map, l_addr), &addr, sizeof(addr));
+ SaveSymData(buff, addr);
+ }
+
+ ReadFromFile(mem_fd, ptr + offsetof(struct link_map, l_next), &ptr, sizeof(ptr));
+ }
+ }
+
+ unsigned long GetStackPointer(int task_nr, std::map<pid_t, user_regs_struct> ®isters) {
+ unsigned long result;
+#if defined(__x86_64__)
+ result = registers[task_nr].rsp;
+#elif defined(__i386__)
+ result = registers[task_nr].esp;
+#elif defined(__arm__)
+ result = registers[task_nr].uregs[13];
+#elif defined(__aarch64__)
+ result = registers[task_nr].sp;
+#else
+#error Unsupported architecture
+#endif
+ return result;
+ }
+
+ void SaveStacks(int mem_fd, std::ofstream &core_file, std::vector<pid_t> tasks, std::map<pid_t, user_regs_struct> registers) {
+ for (auto task_nr : tasks) {
+ unsigned long sp = GetStackPointer(task_nr, registers);
+ auto *entry = FindRecordForMemAddress(sp);
+ size_t len = entry->header.p_vaddr + entry->header.p_memsz - sp;
+ auto out_addr = MemAddr2FileAddr(sp);
+ DumpData(mem_fd, core_file, sp, out_addr, len, "stack nr " + std::to_string(task_nr));
+ }
+ }
+};
+#endif // CORE_HPP__
--- /dev/null
+#ifndef PROCHANDLE_HPP__
+#define PROCHANDLE_HPP__
+
+#include "helpers.hpp"
+#include "core.hpp"
+
+#include <iostream>
+
+typedef enum
+{
+ PS_OK,
+ PS_ERR,
+ PS_BADPID,
+ PS_BADLID,
+ PS_BADADDR,
+ PS_NOSYM,
+ PS_NOFREGS
+} ps_err_e;
+
+struct ps_prochandle {
+ void *core_obj;
+ bool bit32;
+ const int mem_fd;
+ std::ofstream &core_file;
+ const pid_t pid;
+
+ unsigned long int MemAddr2FileAddr(unsigned long int addr) {
+ if (bit32)
+ return static_cast<Core<Elf32>*>(core_obj)->MemAddr2FileAddr(addr);
+ else
+ return static_cast<Core<Elf64>*>(core_obj)->MemAddr2FileAddr(addr);
+ }
+
+ void DumpData(int fd, std::ofstream &output, unsigned long iaddress, unsigned long oaddress, size_t len, const std::string &desc) {
+ if (bit32)
+ static_cast<Core<Elf32>*>(core_obj)->DumpData(fd, output, iaddress, oaddress, len, desc);
+ else
+ static_cast<Core<Elf64>*>(core_obj)->DumpData(fd, output, iaddress, oaddress, len, desc);
+ }
+
+ bool SymAddress(const char *sym_name, unsigned long *addr) {
+ if (bit32)
+ return static_cast<Core<Elf32>*>(core_obj)->SymAddress(sym_name, addr);
+ else
+ return static_cast<Core<Elf64>*>(core_obj)->SymAddress(sym_name, addr);
+ }
+
+ void ReadFromFile(std::ifstream &input, unsigned long address, void *data, size_t len) {
+ if (bit32)
+ static_cast<Core<Elf32>*>(core_obj)->ReadFromFile(input, address, data, len);
+ else
+ static_cast<Core<Elf64>*>(core_obj)->ReadFromFile(input, address, data, len);
+ }
+
+ void ReadFromFile(const int fd, unsigned long address, void *data, size_t len) {
+ if (bit32)
+ static_cast<Core<Elf32>*>(core_obj)->ReadFromFile(fd, address, data, len);
+ else
+ static_cast<Core<Elf64>*>(core_obj)->ReadFromFile(fd, address, data, len);
+ }
+};
+
+extern "C" {
+ ps_err_e ps_pdread(struct ps_prochandle *ph, psaddr_t addr, void *buf, size_t size) {
+ ph->ReadFromFile(ph->mem_fd, reinterpret_cast<unsigned long>(addr), buf, size);
+
+ auto out_addr = ph->MemAddr2FileAddr(reinterpret_cast<unsigned long>(addr));
+ ph->DumpData(ph->mem_fd, ph->core_file, reinterpret_cast<unsigned long>(addr), out_addr, size, "pthread data");
+
+ return PS_OK;
+ }
+
+ ps_err_e ps_pdwrite(struct ps_prochandle *ph, psaddr_t addr, const void *buf, size_t size) {
+ return PS_OK;
+ }
+
+ ps_err_e ps_lgetregs(struct ps_prochandle *ph, lwpid_t lwpid, prgregset_t prgregset) {
+ return PS_OK;
+ }
+
+ ps_err_e ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lwpid, prfpregset_t *prfpregset) {
+ return PS_OK;
+ }
+
+ ps_err_e ps_lsetregs(struct ps_prochandle *ph, lwpid_t lwpid, const prgregset_t prgregset) {
+ return PS_OK;
+ }
+
+ ps_err_e ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lwpid, const prfpregset_t *prfpregset) {
+ return PS_OK;
+ }
+
+ pid_t ps_getpid(struct ps_prochandle *ph) {
+ return ph->pid;
+ }
+
+ ps_err_e ps_pglobal_lookup(struct ps_prochandle *ph, const char *object_name,
+ const char *sym_name, psaddr_t *sym_addr) {
+ unsigned long addr;
+
+ if (!ph->SymAddress(sym_name, &addr))
+ return PS_NOSYM;
+
+ *sym_addr = (psaddr_t)addr;
+ return PS_OK;
+ }
+}
+#endif // PROCHANDLE_HPP__