InstrProfilingPlatformLinux.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*===- InstrProfilingPlatformLinux.c - Profile data Linux platform ------===*\
  2. |*
  3. |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. |* See https://llvm.org/LICENSE.txt for license information.
  5. |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. |*
  7. \*===----------------------------------------------------------------------===*/
  8. #if defined(__linux__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \
  9. (defined(__sun__) && defined(__svr4__)) || defined(__NetBSD__)
  10. #include <elf.h>
  11. #include <link.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include "InstrProfiling.h"
  15. #include "InstrProfilingInternal.h"
  16. #if defined(__FreeBSD__) && !defined(ElfW)
  17. /*
  18. * FreeBSD's elf.h and link.h headers do not define the ElfW(type) macro yet.
  19. * If this is added to all supported FreeBSD versions in the future, this
  20. * compatibility macro can be removed.
  21. */
  22. #define ElfW(type) __ElfN(type)
  23. #endif
  24. #define PROF_DATA_START INSTR_PROF_SECT_START(INSTR_PROF_DATA_COMMON)
  25. #define PROF_DATA_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_DATA_COMMON)
  26. #define PROF_NAME_START INSTR_PROF_SECT_START(INSTR_PROF_NAME_COMMON)
  27. #define PROF_NAME_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_NAME_COMMON)
  28. #define PROF_CNTS_START INSTR_PROF_SECT_START(INSTR_PROF_CNTS_COMMON)
  29. #define PROF_CNTS_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_CNTS_COMMON)
  30. #define PROF_ORDERFILE_START INSTR_PROF_SECT_START(INSTR_PROF_ORDERFILE_COMMON)
  31. #define PROF_VNODES_START INSTR_PROF_SECT_START(INSTR_PROF_VNODES_COMMON)
  32. #define PROF_VNODES_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_VNODES_COMMON)
  33. /* Declare section start and stop symbols for various sections
  34. * generated by compiler instrumentation.
  35. */
  36. extern __llvm_profile_data PROF_DATA_START COMPILER_RT_VISIBILITY
  37. COMPILER_RT_WEAK;
  38. extern __llvm_profile_data PROF_DATA_STOP COMPILER_RT_VISIBILITY
  39. COMPILER_RT_WEAK;
  40. extern char PROF_CNTS_START COMPILER_RT_VISIBILITY COMPILER_RT_WEAK;
  41. extern char PROF_CNTS_STOP COMPILER_RT_VISIBILITY COMPILER_RT_WEAK;
  42. extern uint32_t PROF_ORDERFILE_START COMPILER_RT_VISIBILITY COMPILER_RT_WEAK;
  43. extern char PROF_NAME_START COMPILER_RT_VISIBILITY COMPILER_RT_WEAK;
  44. extern char PROF_NAME_STOP COMPILER_RT_VISIBILITY COMPILER_RT_WEAK;
  45. extern ValueProfNode PROF_VNODES_START COMPILER_RT_VISIBILITY COMPILER_RT_WEAK;
  46. extern ValueProfNode PROF_VNODES_STOP COMPILER_RT_VISIBILITY COMPILER_RT_WEAK;
  47. COMPILER_RT_VISIBILITY const __llvm_profile_data *
  48. __llvm_profile_begin_data(void) {
  49. return &PROF_DATA_START;
  50. }
  51. COMPILER_RT_VISIBILITY const __llvm_profile_data *
  52. __llvm_profile_end_data(void) {
  53. return &PROF_DATA_STOP;
  54. }
  55. COMPILER_RT_VISIBILITY const char *__llvm_profile_begin_names(void) {
  56. return &PROF_NAME_START;
  57. }
  58. COMPILER_RT_VISIBILITY const char *__llvm_profile_end_names(void) {
  59. return &PROF_NAME_STOP;
  60. }
  61. COMPILER_RT_VISIBILITY char *__llvm_profile_begin_counters(void) {
  62. return &PROF_CNTS_START;
  63. }
  64. COMPILER_RT_VISIBILITY char *__llvm_profile_end_counters(void) {
  65. return &PROF_CNTS_STOP;
  66. }
  67. COMPILER_RT_VISIBILITY uint32_t *__llvm_profile_begin_orderfile(void) {
  68. return &PROF_ORDERFILE_START;
  69. }
  70. COMPILER_RT_VISIBILITY ValueProfNode *
  71. __llvm_profile_begin_vnodes(void) {
  72. return &PROF_VNODES_START;
  73. }
  74. COMPILER_RT_VISIBILITY ValueProfNode *__llvm_profile_end_vnodes(void) {
  75. return &PROF_VNODES_STOP;
  76. }
  77. COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = &PROF_VNODES_START;
  78. COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = &PROF_VNODES_STOP;
  79. #ifdef NT_GNU_BUILD_ID
  80. static size_t RoundUp(size_t size, size_t align) {
  81. return (size + align - 1) & ~(align - 1);
  82. }
  83. /*
  84. * Write binary id length and then its data, because binary id does not
  85. * have a fixed length.
  86. */
  87. static int WriteOneBinaryId(ProfDataWriter *Writer, uint64_t BinaryIdLen,
  88. const uint8_t *BinaryIdData,
  89. uint64_t BinaryIdPadding) {
  90. ProfDataIOVec BinaryIdIOVec[] = {
  91. {&BinaryIdLen, sizeof(uint64_t), 1, 0},
  92. {BinaryIdData, sizeof(uint8_t), BinaryIdLen, 0},
  93. {NULL, sizeof(uint8_t), BinaryIdPadding, 1},
  94. };
  95. if (Writer->Write(Writer, BinaryIdIOVec,
  96. sizeof(BinaryIdIOVec) / sizeof(*BinaryIdIOVec)))
  97. return -1;
  98. /* Successfully wrote binary id, report success. */
  99. return 0;
  100. }
  101. /*
  102. * Look for the note that has the name "GNU\0" and type NT_GNU_BUILD_ID
  103. * that contains build id. If build id exists, write binary id.
  104. *
  105. * Each note in notes section starts with a struct which includes
  106. * n_namesz, n_descsz, and n_type members. It is followed by the name
  107. * (whose length is defined in n_namesz) and then by the descriptor
  108. * (whose length is defined in n_descsz).
  109. *
  110. * Note sections like .note.ABI-tag and .note.gnu.build-id are aligned
  111. * to 4 bytes, so round n_namesz and n_descsz to the nearest 4 bytes.
  112. */
  113. static int WriteBinaryIdForNote(ProfDataWriter *Writer,
  114. const ElfW(Nhdr) * Note) {
  115. int BinaryIdSize = 0;
  116. const char *NoteName = (const char *)Note + sizeof(ElfW(Nhdr));
  117. if (Note->n_type == NT_GNU_BUILD_ID && Note->n_namesz == 4 &&
  118. memcmp(NoteName, "GNU\0", 4) == 0) {
  119. uint64_t BinaryIdLen = Note->n_descsz;
  120. const uint8_t *BinaryIdData =
  121. (const uint8_t *)(NoteName + RoundUp(Note->n_namesz, 4));
  122. uint8_t BinaryIdPadding = __llvm_profile_get_num_padding_bytes(BinaryIdLen);
  123. if (Writer != NULL && WriteOneBinaryId(Writer, BinaryIdLen, BinaryIdData,
  124. BinaryIdPadding) == -1)
  125. return -1;
  126. BinaryIdSize = sizeof(BinaryIdLen) + BinaryIdLen + BinaryIdPadding;
  127. }
  128. return BinaryIdSize;
  129. }
  130. /*
  131. * Helper function that iterates through notes section and find build ids.
  132. * If writer is given, write binary ids into profiles.
  133. * If an error happens while writing, return -1.
  134. */
  135. static int WriteBinaryIds(ProfDataWriter *Writer, const ElfW(Nhdr) * Note,
  136. const ElfW(Nhdr) * NotesEnd) {
  137. int BinaryIdsSize = 0;
  138. while (Note < NotesEnd) {
  139. int OneBinaryIdSize = WriteBinaryIdForNote(Writer, Note);
  140. if (OneBinaryIdSize == -1)
  141. return -1;
  142. BinaryIdsSize += OneBinaryIdSize;
  143. /* Calculate the offset of the next note in notes section. */
  144. size_t NoteOffset = sizeof(ElfW(Nhdr)) + RoundUp(Note->n_namesz, 4) +
  145. RoundUp(Note->n_descsz, 4);
  146. Note = (const ElfW(Nhdr) *)((const char *)(Note) + NoteOffset);
  147. }
  148. return BinaryIdsSize;
  149. }
  150. /*
  151. * Write binary ids into profiles if writer is given.
  152. * Return the total size of binary ids.
  153. * If an error happens while writing, return -1.
  154. */
  155. COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) {
  156. extern const ElfW(Ehdr) __ehdr_start __attribute__((visibility("hidden")));
  157. const ElfW(Ehdr) *ElfHeader = &__ehdr_start;
  158. const ElfW(Phdr) *ProgramHeader =
  159. (const ElfW(Phdr) *)((uintptr_t)ElfHeader + ElfHeader->e_phoff);
  160. int TotalBinaryIdsSize = 0;
  161. uint32_t I;
  162. /* Iterate through entries in the program header. */
  163. for (I = 0; I < ElfHeader->e_phnum; I++) {
  164. /* Look for the notes segment in program header entries. */
  165. if (ProgramHeader[I].p_type != PT_NOTE)
  166. continue;
  167. /* There can be multiple notes segment, and examine each of them. */
  168. const ElfW(Nhdr) * Note;
  169. const ElfW(Nhdr) * NotesEnd;
  170. /*
  171. * When examining notes in file, use p_offset, which is the offset within
  172. * the elf file, to find the start of notes.
  173. */
  174. if (ProgramHeader[I].p_memsz == 0 ||
  175. ProgramHeader[I].p_memsz == ProgramHeader[I].p_filesz) {
  176. Note = (const ElfW(Nhdr) *)((uintptr_t)ElfHeader +
  177. ProgramHeader[I].p_offset);
  178. NotesEnd = (const ElfW(Nhdr) *)((const char *)(Note) +
  179. ProgramHeader[I].p_filesz);
  180. } else {
  181. /*
  182. * When examining notes in memory, use p_vaddr, which is the address of
  183. * section after loaded to memory, to find the start of notes.
  184. */
  185. Note =
  186. (const ElfW(Nhdr) *)((uintptr_t)ElfHeader + ProgramHeader[I].p_vaddr);
  187. NotesEnd =
  188. (const ElfW(Nhdr) *)((const char *)(Note) + ProgramHeader[I].p_memsz);
  189. }
  190. int BinaryIdsSize = WriteBinaryIds(Writer, Note, NotesEnd);
  191. if (TotalBinaryIdsSize == -1)
  192. return -1;
  193. TotalBinaryIdsSize += BinaryIdsSize;
  194. }
  195. return TotalBinaryIdsSize;
  196. }
  197. #else /* !NT_GNU_BUILD_ID */
  198. /*
  199. * Fallback implementation for targets that don't support the GNU
  200. * extensions NT_GNU_BUILD_ID and __ehdr_start.
  201. */
  202. COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) {
  203. return 0;
  204. }
  205. #endif
  206. #endif