LineProgram.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "LineProgram.h"
  27. #include <AK/Debug.h>
  28. #include <AK/String.h>
  29. #include <AK/StringBuilder.h>
  30. namespace Debug::Dwarf {
  31. LineProgram::LineProgram(InputMemoryStream& stream)
  32. : m_stream(stream)
  33. {
  34. m_unit_offset = m_stream.offset();
  35. parse_unit_header();
  36. parse_source_directories();
  37. parse_source_files();
  38. run_program();
  39. }
  40. void LineProgram::parse_unit_header()
  41. {
  42. m_stream >> Bytes { &m_unit_header, sizeof(m_unit_header) };
  43. VERIFY(m_unit_header.version == DWARF_VERSION);
  44. VERIFY(m_unit_header.opcode_base == SPECIAL_OPCODES_BASE);
  45. #if DWARF_DEBUG
  46. dbgln("unit length: {}", m_unit_header.length);
  47. #endif
  48. }
  49. void LineProgram::parse_source_directories()
  50. {
  51. m_source_directories.append(".");
  52. while (m_stream.peek_or_error()) {
  53. String directory;
  54. m_stream >> directory;
  55. #if DWARF_DEBUG
  56. dbgln("directory: {}", directory);
  57. #endif
  58. m_source_directories.append(move(directory));
  59. }
  60. m_stream.handle_recoverable_error();
  61. m_stream.discard_or_error(1);
  62. VERIFY(!m_stream.has_any_error());
  63. }
  64. void LineProgram::parse_source_files()
  65. {
  66. m_source_files.append({ ".", 0 });
  67. while (!m_stream.eof() && m_stream.peek_or_error()) {
  68. String file_name;
  69. m_stream >> file_name;
  70. size_t directory_index = 0;
  71. m_stream.read_LEB128_unsigned(directory_index);
  72. size_t _unused = 0;
  73. m_stream.read_LEB128_unsigned(_unused); // skip modification time
  74. m_stream.read_LEB128_unsigned(_unused); // skip file size
  75. #if DWARF_DEBUG
  76. dbgln("file: {}, directory index: {}", file_name, directory_index);
  77. #endif
  78. m_source_files.append({ file_name, directory_index });
  79. }
  80. m_stream.discard_or_error(1);
  81. VERIFY(!m_stream.has_any_error());
  82. }
  83. void LineProgram::append_to_line_info()
  84. {
  85. #if DWARF_DEBUG
  86. dbgln("appending line info: {:p}, {}:{}", m_address, m_source_files[m_file_index].name, m_line);
  87. #endif
  88. if (!m_is_statement)
  89. return;
  90. if (m_file_index >= m_source_files.size())
  91. return;
  92. String directory = m_source_directories[m_source_files[m_file_index].directory_index];
  93. StringBuilder full_path(directory.length() + m_source_files[m_file_index].name.length() + 1);
  94. full_path.append(directory);
  95. full_path.append('/');
  96. full_path.append(m_source_files[m_file_index].name);
  97. m_lines.append({ m_address, full_path.to_string(), m_line });
  98. }
  99. void LineProgram::reset_registers()
  100. {
  101. m_address = 0;
  102. m_line = 1;
  103. m_file_index = 1;
  104. m_is_statement = m_unit_header.default_is_stmt == 1;
  105. }
  106. void LineProgram::handle_extended_opcode()
  107. {
  108. size_t length = 0;
  109. m_stream.read_LEB128_unsigned(length);
  110. u8 sub_opcode = 0;
  111. m_stream >> sub_opcode;
  112. switch (sub_opcode) {
  113. case ExtendedOpcodes::EndSequence: {
  114. append_to_line_info();
  115. reset_registers();
  116. break;
  117. }
  118. case ExtendedOpcodes::SetAddress: {
  119. VERIFY(length == sizeof(size_t) + 1);
  120. m_stream >> m_address;
  121. #if DWARF_DEBUG
  122. dbgln("SetAddress: {:p}", m_address);
  123. #endif
  124. break;
  125. }
  126. case ExtendedOpcodes::SetDiscriminator: {
  127. #if DWARF_DEBUG
  128. dbgln("SetDiscriminator");
  129. #endif
  130. m_stream.discard_or_error(1);
  131. break;
  132. }
  133. default:
  134. #if DWARF_DEBUG
  135. dbgln("offset: {:p}", m_stream.offset());
  136. #endif
  137. VERIFY_NOT_REACHED();
  138. }
  139. }
  140. void LineProgram::handle_standard_opcode(u8 opcode)
  141. {
  142. switch (opcode) {
  143. case StandardOpcodes::Copy: {
  144. append_to_line_info();
  145. break;
  146. }
  147. case StandardOpcodes::AdvancePc: {
  148. size_t operand = 0;
  149. m_stream.read_LEB128_unsigned(operand);
  150. size_t delta = operand * m_unit_header.min_instruction_length;
  151. #if DWARF_DEBUG
  152. dbgln("AdvancePC by: {} to: {:p}", delta, m_address + delta);
  153. #endif
  154. m_address += delta;
  155. break;
  156. }
  157. case StandardOpcodes::SetFile: {
  158. size_t new_file_index = 0;
  159. m_stream.read_LEB128_unsigned(new_file_index);
  160. #if DWARF_DEBUG
  161. dbgln("SetFile: new file index: {}", new_file_index);
  162. #endif
  163. m_file_index = new_file_index;
  164. break;
  165. }
  166. case StandardOpcodes::SetColumn: {
  167. // not implemented
  168. #if DWARF_DEBUG
  169. dbgln("SetColumn");
  170. #endif
  171. size_t new_column;
  172. m_stream.read_LEB128_unsigned(new_column);
  173. break;
  174. }
  175. case StandardOpcodes::AdvanceLine: {
  176. ssize_t line_delta;
  177. m_stream.read_LEB128_signed(line_delta);
  178. VERIFY(line_delta >= 0 || m_line >= (size_t)(-line_delta));
  179. m_line += line_delta;
  180. #if DWARF_DEBUG
  181. dbgln("AdvanceLine: {}", m_line);
  182. #endif
  183. break;
  184. }
  185. case StandardOpcodes::NegateStatement: {
  186. #if DWARF_DEBUG
  187. dbgln("NegateStatement");
  188. #endif
  189. m_is_statement = !m_is_statement;
  190. break;
  191. }
  192. case StandardOpcodes::ConstAddPc: {
  193. u8 adjusted_opcode = 255 - SPECIAL_OPCODES_BASE;
  194. ssize_t address_increment = (adjusted_opcode / m_unit_header.line_range) * m_unit_header.min_instruction_length;
  195. address_increment *= m_unit_header.min_instruction_length;
  196. #if DWARF_DEBUG
  197. dbgln("ConstAddPc: advance pc by: {} to: {}", address_increment, (m_address + address_increment));
  198. #endif
  199. m_address += address_increment;
  200. break;
  201. }
  202. case StandardOpcodes::SetIsa: {
  203. size_t isa;
  204. m_stream.read_LEB128_unsigned(isa);
  205. dbgln("SetIsa: {}", isa);
  206. break;
  207. }
  208. case StandardOpcodes::FixAdvancePc: {
  209. u16 delta = 0;
  210. m_stream >> delta;
  211. #if DWARF_DEBUG
  212. dbgln("FixAdvancePC by: {} to: {:p}", delta, m_address + delta);
  213. #endif
  214. m_address += delta;
  215. break;
  216. }
  217. default:
  218. dbgln("Unhandled LineProgram opcode {}", opcode);
  219. VERIFY_NOT_REACHED();
  220. }
  221. }
  222. void LineProgram::handle_special_opcode(u8 opcode)
  223. {
  224. u8 adjusted_opcode = opcode - SPECIAL_OPCODES_BASE;
  225. ssize_t address_increment = (adjusted_opcode / m_unit_header.line_range) * m_unit_header.min_instruction_length;
  226. ssize_t line_increment = m_unit_header.line_base + (adjusted_opcode % m_unit_header.line_range);
  227. m_address += address_increment;
  228. m_line += line_increment;
  229. if constexpr (DWARF_DEBUG) {
  230. dbgln("Special adjusted_opcode: {}, address_increment: {}, line_increment: {}", adjusted_opcode, address_increment, line_increment);
  231. dbgln("Address is now: {:p}, and line is: {}:{}", m_address, m_source_files[m_file_index].name, m_line);
  232. }
  233. append_to_line_info();
  234. }
  235. void LineProgram::run_program()
  236. {
  237. reset_registers();
  238. while ((size_t)m_stream.offset() < m_unit_offset + sizeof(u32) + m_unit_header.length) {
  239. u8 opcode = 0;
  240. m_stream >> opcode;
  241. dbgln_if(DWARF_DEBUG, "{:p}: opcode: {}", m_stream.offset() - 1, opcode);
  242. if (opcode == 0) {
  243. handle_extended_opcode();
  244. } else if (opcode >= 1 && opcode <= 12) {
  245. handle_standard_opcode(opcode);
  246. } else {
  247. handle_special_opcode(opcode);
  248. }
  249. }
  250. }
  251. }