/src/dfinstancelinux.cpp

https://code.google.com/p/dwarftherapist/ · C++ · 411 lines · 301 code · 42 blank · 68 comment · 58 complexity · eb1e220f55407b8cf93c6d321f32531b MD5 · raw file

  1. /*
  2. Dwarf Therapist
  3. Copyright (c) 2009 Trey Stout (chmod)
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. */
  20. #include <QtGui>
  21. #include <QtDebug>
  22. #include <sys/ptrace.h>
  23. #include <errno.h>
  24. #include <wait.h>
  25. #include "dfinstance.h"
  26. #include "dfinstancelinux.h"
  27. #include "defines.h"
  28. #include "dwarf.h"
  29. #include "utils.h"
  30. #include "gamedatareader.h"
  31. #include "memorylayout.h"
  32. #include "cp437codec.h"
  33. #include "memorysegment.h"
  34. #include "truncatingfilelogger.h"
  35. DFInstanceLinux::DFInstanceLinux(QObject* parent)
  36. : DFInstance(parent)
  37. {
  38. }
  39. DFInstanceLinux::~DFInstanceLinux() {
  40. if (m_attach_count > 0) {
  41. detach();
  42. }
  43. }
  44. QVector<uint> DFInstanceLinux::enumerate_vector(const uint &addr) {
  45. QVector<uint> addrs;
  46. if (!addr)
  47. return addrs;
  48. attach();
  49. VIRTADDR start = read_addr(addr);
  50. VIRTADDR end = read_addr(addr + 4);
  51. int bytes = end - start;
  52. int entries = bytes / 4;
  53. TRACE << "enumerating vector at" << hex << addr << "START" << start
  54. << "END" << end << "UNVERIFIED ENTRIES" << dec << entries;
  55. VIRTADDR tmp_addr = 0;
  56. if (entries > 5000) {
  57. LOGW << "vector at" << hexify(addr) << "has over 5000 entries! (" <<
  58. entries << ")";
  59. }
  60. #ifdef _DEBUG
  61. if (m_layout->is_complete()) {
  62. Q_ASSERT_X(start > 0, "enumerate_vector", "start pointer must be larger than 0");
  63. Q_ASSERT_X(end > 0, "enumerate_vector", "End must be larger than start!");
  64. Q_ASSERT_X(start % 4 == 0, "enumerate_vector", "Start must be divisible by 4");
  65. Q_ASSERT_X(end % 4 == 0, "enumerate_vector", "End must be divisible by 4");
  66. Q_ASSERT_X(end >= start, "enumerate_vector", "End must be >= start!");
  67. Q_ASSERT_X((end - start) % 4 == 0, "enumerate_vector", "end - start must be divisible by 4");
  68. } else {
  69. // when testing it's usually pretty bad to find a vector with more
  70. // than 5000 entries... so throw
  71. Q_ASSERT_X(entries < 5000, "enumerate_vector", "more than 5000 entires");
  72. }
  73. #endif
  74. QByteArray data(bytes, 0);
  75. int bytes_read = read_raw(start, bytes, data);
  76. if (bytes_read != bytes && m_layout->is_complete()) {
  77. LOGW << "Tried to read" << bytes << "bytes but only got"
  78. << bytes_read;
  79. detach();
  80. return addrs;
  81. }
  82. for(int i = 0; i < bytes; i += 4) {
  83. tmp_addr = decode_dword(data.mid(i, 4));
  84. if (m_layout->is_complete()) {
  85. if (is_valid_address(tmp_addr))
  86. addrs << tmp_addr;
  87. } else {
  88. addrs << tmp_addr;
  89. }
  90. }
  91. detach();
  92. return addrs;
  93. }
  94. uint DFInstanceLinux::calculate_checksum() {
  95. // ELF binaries don't seem to store a linker timestamp, so just MD5 the file.
  96. uint md5 = 0; // we're going to throw away a lot of this checksum we just need 4bytes worth
  97. QProcess *proc = new QProcess(this);
  98. QStringList args;
  99. args << "md5sum";
  100. args << QString("/proc/%1/exe").arg(m_pid);
  101. proc->start("/usr/bin/env", args);
  102. if (proc->waitForReadyRead(3000)) {
  103. QByteArray out = proc->readAll();
  104. QString str_md5(out);
  105. QStringList chunks = str_md5.split(" ");
  106. str_md5 = chunks[0];
  107. bool ok;
  108. md5 = str_md5.mid(0, 8).toUInt(&ok,16); // use the first 8 bytes
  109. TRACE << "GOT MD5:" << md5;
  110. }
  111. return md5;
  112. }
  113. QString DFInstanceLinux::read_string(const VIRTADDR &addr) {
  114. VIRTADDR buffer_addr = read_addr(addr);
  115. int upper_size = 256;
  116. QByteArray buf(upper_size, 0);
  117. read_raw(buffer_addr, upper_size, buf);
  118. QString ret_val(buf);
  119. CP437Codec *codec = new CP437Codec;
  120. ret_val = codec->toUnicode(ret_val.toAscii());
  121. return ret_val;
  122. }
  123. int DFInstanceLinux::write_string(const VIRTADDR &addr, const QString &str) {
  124. Q_UNUSED(addr);
  125. Q_UNUSED(str);
  126. return 0;
  127. }
  128. int DFInstanceLinux::write_int(const VIRTADDR &addr, const int &val) {
  129. return write_raw(addr, sizeof(int), (void*)&val);
  130. }
  131. bool DFInstanceLinux::attach() {
  132. TRACE << "STARTING ATTACH" << m_attach_count;
  133. if (is_attached()) {
  134. m_attach_count++;
  135. TRACE << "ALREADY ATTACHED SKIPPING..." << m_attach_count;
  136. return true;
  137. }
  138. if (ptrace(PTRACE_ATTACH, m_pid, 0, 0) == -1) { // unable to attach
  139. perror("ptrace attach");
  140. LOGE << "Could not attach to PID" << m_pid;
  141. return false;
  142. }
  143. int status;
  144. while(true) {
  145. TRACE << "waiting for proc to stop";
  146. pid_t w = waitpid(m_pid, &status, 0);
  147. if (w == -1) {
  148. // child died
  149. perror("wait inside attach()");
  150. LOGE << "child died?";
  151. exit(-1);
  152. }
  153. if (WIFSTOPPED(status)) {
  154. break;
  155. }
  156. TRACE << "waitpid returned but child wasn't stopped, keep waiting...";
  157. }
  158. m_attach_count++;
  159. TRACE << "FINISHED ATTACH" << m_attach_count;
  160. return m_attach_count > 0;
  161. }
  162. bool DFInstanceLinux::detach() {
  163. TRACE << "STARTING DETACH" << m_attach_count;
  164. m_attach_count--;
  165. if (m_attach_count > 0) {
  166. TRACE << "NO NEED TO DETACH SKIPPING..." << m_attach_count;
  167. return true;
  168. }
  169. ptrace(PTRACE_DETACH, m_pid, 0, 0);
  170. TRACE << "FINISHED DETACH" << m_attach_count;
  171. return m_attach_count > 0;
  172. }
  173. int DFInstanceLinux::read_raw(const VIRTADDR &addr, int bytes, QByteArray &buffer) {
  174. // try to attach, will be ignored if we're already attached
  175. attach();
  176. // open the memory virtual file for this proc (can only read once
  177. // attached and child is stopped
  178. QFile mem_file(QString("/proc/%1/mem").arg(m_pid));
  179. if (!mem_file.open(QIODevice::ReadOnly)) {
  180. LOGE << "Unable to open" << mem_file.fileName();
  181. detach();
  182. return 0;
  183. }
  184. int bytes_read = 0; // tracks how much we've read of what was asked for
  185. int step_size = 0x1000; // how many bytes to read each step
  186. QByteArray chunk(step_size, 0); // our temporary memory container
  187. buffer.fill(0, bytes); // zero our buffer
  188. int steps = bytes / step_size;
  189. if (bytes % step_size)
  190. steps++;
  191. for(VIRTADDR ptr = addr; ptr < addr+ bytes; ptr += step_size) {
  192. if (ptr+step_size > addr + bytes)
  193. step_size = addr + bytes - ptr;
  194. mem_file.seek(ptr);
  195. chunk = mem_file.read(step_size);
  196. buffer.replace(bytes_read, chunk.size(), chunk);
  197. bytes_read += chunk.size();
  198. }
  199. mem_file.close();
  200. detach();
  201. return bytes_read;
  202. }
  203. int DFInstanceLinux::write_raw(const VIRTADDR &addr, const int &bytes,
  204. void *buffer) {
  205. // try to attach, will be ignored if we're already attached
  206. attach();
  207. /* Since most kernels won't let us write to /proc/<pid>/mem, we have to poke
  208. * out data in n bytes at a time. Good thing we read way more than we write.
  209. *
  210. * On x86-64 systems, the size that POKEDATA writes is 8 bytes instead of
  211. * 4, so we need to use the sizeof( long ) as our step size.
  212. */
  213. // TODO: Should probably have a global define of word size for the
  214. // architecture being compiled on. For now, sizeof(long) is consistent
  215. // on most (all?) linux systems so we'll keep this.
  216. uint stepsize = sizeof( long );
  217. uint bytes_written = 0; // keep track of how much we've written
  218. uint steps = bytes / stepsize;
  219. if (bytes % stepsize)
  220. steps++;
  221. LOGD << "WRITE_RAW: WILL WRITE" << bytes << "bytes over" << steps << "steps, with stepsize " << stepsize;
  222. // we want to make sure that given the case where (bytes % stepsize != 0) we don't
  223. // clobber data past where we meant to write. So we're first going to read
  224. // the existing data as it is, and then write the changes over the existing
  225. // data in the buffer first, then write the buffer with stepsize bytes at a time
  226. // to the process. This should ensure no clobbering of data.
  227. QByteArray existing_data(steps * stepsize, 0);
  228. read_raw(addr, (steps * stepsize), existing_data);
  229. LOGD << "WRITE_RAW: EXISTING OLD DATA " << existing_data.toHex();
  230. // ok we have our insurance in place, now write our new junk to the buffer
  231. memcpy(existing_data.data(), buffer, bytes);
  232. QByteArray tmp2(existing_data);
  233. LOGD << "WRITE_RAW: EXISTING WITH NEW DATA" << tmp2.toHex();
  234. // ok, now our to be written data is in part or all of the exiting data buffer
  235. long tmp_data;
  236. for (uint i = 0; i < steps; ++i) {
  237. int offset = i * stepsize;
  238. // for each step write a single word to the child
  239. memcpy(&tmp_data, existing_data.mid(offset, stepsize).data(), stepsize);
  240. QByteArray tmp_data_str((char*)&tmp_data, stepsize);
  241. LOGD << "WRITE_RAW:" << hex << addr + offset << "HEX" << tmp_data_str.toHex();
  242. if (ptrace(PTRACE_POKEDATA, m_pid, addr + offset, tmp_data) != 0) {
  243. perror("write word");
  244. break;
  245. } else {
  246. bytes_written += stepsize;
  247. }
  248. long written = ptrace(PTRACE_PEEKDATA, m_pid, addr + offset, 0);
  249. QByteArray foo((char*)&written, stepsize);
  250. LOGD << "WRITE_RAW: WE APPEAR TO HAVE WRITTEN" << foo.toHex();
  251. }
  252. // attempt to detach, will be ignored if we're several layers into an attach chain
  253. detach();
  254. // tell the caller how many bytes we wrote
  255. return bytes_written;
  256. }
  257. bool DFInstanceLinux::find_running_copy(bool connect_anyway) {
  258. // find PID of DF
  259. TRACE << "attempting to find running copy of DF by executable name";
  260. QProcess *proc = new QProcess(this);
  261. QStringList args;
  262. args << "dwarfort.exe"; // 0.31.04 and earlier
  263. args << "Dwarf_Fortress"; // 0.31.05+
  264. proc->start("pidof", args);
  265. proc->waitForFinished(1000);
  266. if (proc->exitCode() == 0) { //found it
  267. QByteArray out = proc->readAllStandardOutput();
  268. QString str_pid(out);
  269. m_pid = str_pid.toInt();
  270. m_memory_file.setFileName(QString("/proc/%1/mem").arg(m_pid));
  271. TRACE << "FOUND PID:" << m_pid;
  272. } else {
  273. QMessageBox::warning(0, tr("Warning"),
  274. tr("Unable to locate a running copy of Dwarf "
  275. "Fortress, are you sure it's running?"));
  276. LOGW << "can't find running copy";
  277. m_is_ok = false;
  278. return m_is_ok;
  279. }
  280. map_virtual_memory();
  281. //qDebug() << "LOWEST ADDR:" << hex << lowest_addr;
  282. //DUMP LIST OF MEMORY RANGES
  283. /*
  284. QPair<uint, uint> tmp_pair;
  285. foreach(tmp_pair, m_regions) {
  286. LOGD << "RANGE start:" << hex << tmp_pair.first << "end:" << tmp_pair.second;
  287. }*/
  288. VIRTADDR m_base_addr = read_addr(m_lowest_address + 0x18);
  289. LOGD << "base_addr:" << m_base_addr << "HEX" << hex << m_base_addr;
  290. m_is_ok = m_base_addr > 0;
  291. uint checksum = calculate_checksum();
  292. LOGD << "DF's checksum is" << hexify(checksum);
  293. if (m_is_ok) {
  294. m_layout = get_memory_layout(hexify(checksum).toLower(), !connect_anyway);
  295. }
  296. //Get dwarf fortress directory
  297. m_df_dir = QDir(QFileInfo(QString("/proc/%1/cwd").arg(m_pid)).symLinkTarget());
  298. LOGI << "Dwarf fortress path:" << m_df_dir.absolutePath();
  299. return m_is_ok || connect_anyway;
  300. }
  301. void DFInstanceLinux::map_virtual_memory() {
  302. // destroy existing segments
  303. foreach(MemorySegment *seg, m_regions) {
  304. delete(seg);
  305. }
  306. m_regions.clear();
  307. if (!m_is_ok)
  308. return;
  309. // scan the maps to populate known regions of memory
  310. QFile f(QString("/proc/%1/maps").arg(m_pid));
  311. if (!f.open(QIODevice::ReadOnly)) {
  312. LOGE << "Unable to open" << f.fileName();
  313. return;
  314. }
  315. TRACE << "opened" << f.fileName();
  316. QByteArray line;
  317. m_lowest_address = 0xFFFFFFFF;
  318. m_highest_address = 0;
  319. uint start_addr = 0;
  320. uint end_addr = 0;
  321. bool ok;
  322. QRegExp rx("^([a-f\\d]+)-([a-f\\d]+)\\s([rwxsp-]{4})\\s+[\\d\\w]{8}\\s+[\\d\\w]{2}:[\\d\\w]{2}\\s+(\\d+)\\s*(.+)\\s*$");
  323. do {
  324. line = f.readLine();
  325. // parse the first line to see find the base
  326. if (rx.indexIn(line) != -1) {
  327. //LOGD << "RANGE" << rx.cap(1) << "-" << rx.cap(2) << "PERMS" <<
  328. // rx.cap(3) << "INODE" << rx.cap(4) << "PATH" << rx.cap(5);
  329. start_addr = rx.cap(1).toUInt(&ok, 16);
  330. end_addr = rx.cap(2).toUInt(&ok, 16);
  331. QString perms = rx.cap(3).trimmed();
  332. int inode = rx.cap(4).toInt();
  333. QString path = rx.cap(5).trimmed();
  334. //LOGD << "RANGE" << hex << start_addr << "-" << end_addr << perms
  335. // << inode << "PATH >" << path << "<";
  336. bool keep_it = false;
  337. if (path.contains("[heap]") || path.contains("[stack]") || path.contains("[vdso]")) {
  338. keep_it = true;
  339. } else if (perms.contains("r") && inode && path == QFile::symLinkTarget(QString("/proc/%1/exe").arg(m_pid))) {
  340. keep_it = true;
  341. } else {
  342. keep_it = path.isEmpty();
  343. }
  344. // uncomment to search HEAP only
  345. //keep_it = path.contains("[heap]");
  346. keep_it = true;
  347. if (keep_it && end_addr > start_addr) {
  348. MemorySegment *segment = new MemorySegment(path, start_addr, end_addr);
  349. TRACE << "keeping" << segment->to_string();
  350. m_regions << segment;
  351. if (start_addr < m_lowest_address)
  352. m_lowest_address = start_addr;
  353. else if (end_addr > m_highest_address)
  354. m_highest_address = end_addr;
  355. }
  356. if (path.contains("[heap]")) {
  357. //LOGD << "setting heap start address at" << hex << start_addr;
  358. m_heap_start_address = start_addr;
  359. }
  360. }
  361. } while (!line.isEmpty());
  362. f.close();
  363. }
  364. bool DFInstance::authorize() {
  365. return true;
  366. }