| 1 | /*===- InstrProfilingPlatformAIX.c - Profile data AIX 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 | |
| 9 | #if defined(_AIX) |
| 10 | |
| 11 | #ifdef __64BIT__ |
| 12 | #define __XCOFF64__ |
| 13 | #endif |
| 14 | #include <errno.h> |
| 15 | #include <stdlib.h> |
| 16 | #include <string.h> |
| 17 | #include <sys/ldr.h> |
| 18 | #include <xcoff.h> |
| 19 | |
| 20 | #include "InstrProfiling.h" |
| 21 | #include "InstrProfilingInternal.h" |
| 22 | |
| 23 | #define BIN_ID_PREFIX "xcoff_binary_id:" |
| 24 | |
| 25 | // If found, write the build-id into the Result buffer. |
| 26 | static size_t FindBinaryId(char *Result, size_t Size) { |
| 27 | unsigned long EntryAddr = (unsigned long)__builtin_return_address(0); |
| 28 | |
| 29 | // Use loadquery to get information about loaded modules; loadquery writes |
| 30 | // its result into a buffer of unknown size. |
| 31 | char Buf[1024]; |
| 32 | size_t BufSize = sizeof(Buf); |
| 33 | char *BufPtr = Buf; |
| 34 | int RC = -1; |
| 35 | |
| 36 | errno = 0; |
| 37 | RC = loadquery(L_GETXINFO | L_IGNOREUNLOAD, BufPtr, (unsigned int)BufSize); |
| 38 | if (RC == -1 && errno == ENOMEM) { |
| 39 | BufSize = 64000; // should be plenty for any program. |
| 40 | BufPtr = malloc(BufSize); |
| 41 | if (BufPtr != 0) |
| 42 | RC = loadquery(L_GETXINFO | L_IGNOREUNLOAD, BufPtr, (unsigned int)BufSize); |
| 43 | } |
| 44 | |
| 45 | if (RC == -1) |
| 46 | goto done; |
| 47 | |
| 48 | // Locate the ld_xinfo corresponding to this module. |
| 49 | struct ld_xinfo *CurInfo = (struct ld_xinfo *)BufPtr; |
| 50 | while (1) { |
| 51 | unsigned long CurTextStart = (uint64_t)CurInfo->ldinfo_textorg; |
| 52 | unsigned long CurTextEnd = CurTextStart + CurInfo->ldinfo_textsize; |
| 53 | if (CurTextStart <= EntryAddr && EntryAddr < CurTextEnd) { |
| 54 | // Found my slot. Now search for the build-id. |
| 55 | char *p = (char *)CurInfo->ldinfo_textorg; |
| 56 | |
| 57 | FILHDR *f = (FILHDR *)p; |
| 58 | AOUTHDR *a = (AOUTHDR *)(p + FILHSZ); |
| 59 | SCNHDR *s = |
| 60 | (SCNHDR *)(p + FILHSZ + f->f_opthdr + SCNHSZ * (a->o_snloader - 1)); |
| 61 | LDHDR *ldhdr = (LDHDR *)(p + s->s_scnptr); |
| 62 | // This is the loader string table |
| 63 | char *lstr = (char *)ldhdr + ldhdr->l_stoff; |
| 64 | |
| 65 | // If the build-id exists, it's the first entry. |
| 66 | // Each entry is comprised of a 2-byte size component, followed by the |
| 67 | // data. |
| 68 | size_t len = *(short *)lstr; |
| 69 | char *str = (char *)(lstr + 2); |
| 70 | size_t PrefixLen = sizeof(BIN_ID_PREFIX) - 1; |
| 71 | if (len > PrefixLen && (len - PrefixLen) <= Size && |
| 72 | strncmp(str, BIN_ID_PREFIX, PrefixLen) == 0) { |
| 73 | memcpy(Result, str + PrefixLen, len - PrefixLen); |
| 74 | RC = len - PrefixLen; |
| 75 | goto done; |
| 76 | } |
| 77 | break; |
| 78 | } |
| 79 | if (CurInfo->ldinfo_next == 0u) |
| 80 | break; |
| 81 | CurInfo = (struct ld_xinfo *)((char *)CurInfo + CurInfo->ldinfo_next); |
| 82 | } |
| 83 | done: |
| 84 | if (BufSize != sizeof(Buf) && BufPtr != 0) |
| 85 | free(BufPtr); |
| 86 | return RC; |
| 87 | } |
| 88 | |
| 89 | static int StrToHexError = 0; |
| 90 | static uint8_t StrToHex(char c) { |
| 91 | if (c >= '0' && c <= '9') |
| 92 | return c - '0'; |
| 93 | if (c >= 'a' && c <= 'f') |
| 94 | return c - 'a' + 0xa; |
| 95 | if (c >= 'A' && c <= 'F') |
| 96 | return c - 'A' + 0xa; |
| 97 | StrToHexError = 1; |
| 98 | return 0; |
| 99 | } |
| 100 | |
| 101 | COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) { |
| 102 | // 200 bytes should be enough for the build-id hex string. |
| 103 | static char Buf[200]; |
| 104 | // Profile reading tools expect this to be 8-bytes long. |
| 105 | static int64_t BinaryIdLen = 0; |
| 106 | static uint8_t *BinaryIdData = 0; |
| 107 | |
| 108 | // -1 means we already checked for a BinaryId and didn't find one. |
| 109 | if (BinaryIdLen == -1) |
| 110 | return 0; |
| 111 | |
| 112 | // Are we being called for the first time? |
| 113 | if (BinaryIdLen == 0) { |
| 114 | if (getenv("LLVM_PROFILE_NO_BUILD_ID" )) |
| 115 | goto fail; |
| 116 | |
| 117 | int BuildIdLen = FindBinaryId(Buf, sizeof(Buf)); |
| 118 | if (BuildIdLen <= 0) |
| 119 | goto fail; |
| 120 | |
| 121 | if (Buf[BuildIdLen - 1] == '\0') |
| 122 | BuildIdLen--; |
| 123 | |
| 124 | // assume even number of digits/chars, so 0xabc must be 0x0abc |
| 125 | if ((BuildIdLen % 2) != 0 || BuildIdLen == 0) |
| 126 | goto fail; |
| 127 | |
| 128 | // The numeric ID is represented as an ascii string in the loader section, |
| 129 | // so convert it to raw binary. |
| 130 | BinaryIdLen = BuildIdLen / 2; |
| 131 | BinaryIdData = (uint8_t *)Buf; |
| 132 | |
| 133 | // Skip "0x" prefix if it exists. |
| 134 | if (Buf[0] == '0' && Buf[1] == 'x') { |
| 135 | BinaryIdLen -= 1; |
| 136 | BinaryIdData += 2; |
| 137 | } |
| 138 | |
| 139 | StrToHexError = 0; |
| 140 | for (int i = 0; i < BinaryIdLen; i++) |
| 141 | BinaryIdData[i] = (StrToHex(BinaryIdData[2 * i]) << 4) + |
| 142 | StrToHex(BinaryIdData[2 * i + 1]); |
| 143 | |
| 144 | if (StrToHexError) |
| 145 | goto fail; |
| 146 | |
| 147 | if (getenv("LLVM_PROFILE_VERBOSE" )) { |
| 148 | char *StrBuf = (char *)COMPILER_RT_ALLOCA(2 * BinaryIdLen + 1); |
| 149 | for (int i = 0; i < (int)BinaryIdLen; i++) |
| 150 | sprintf(&StrBuf[2 * i], "%02x" , BinaryIdData[i]); |
| 151 | PROF_NOTE("Writing binary id: %s\n" , StrBuf); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | uint8_t BinaryIdPadding = __llvm_profile_get_num_padding_bytes(BinaryIdLen); |
| 156 | if (Writer && lprofWriteOneBinaryId(Writer, BinaryIdLen, BinaryIdData, |
| 157 | BinaryIdPadding) == -1) |
| 158 | return -1; // Return -1 rather goto fail to match the NT_GNU_BUILD_ID path. |
| 159 | |
| 160 | return sizeof(BinaryIdLen) + BinaryIdLen + BinaryIdPadding; |
| 161 | |
| 162 | fail: |
| 163 | if (getenv("LLVM_PROFILE_VERBOSE" )) |
| 164 | fprintf(stderr, "no or invalid binary id: %.*s\n" , (int)sizeof(Buf), Buf); |
| 165 | BinaryIdLen = -1; |
| 166 | return 0; |
| 167 | } |
| 168 | |
| 169 | // Empty stubs to allow linking object files using the registration-based scheme |
| 170 | COMPILER_RT_VISIBILITY |
| 171 | void __llvm_profile_register_function(void *Data_) {} |
| 172 | |
| 173 | COMPILER_RT_VISIBILITY |
| 174 | void __llvm_profile_register_names_function(void *NamesStart, |
| 175 | uint64_t NamesSize) {} |
| 176 | |
| 177 | // The __start_SECNAME and __stop_SECNAME symbols (for SECNAME \in |
| 178 | // {"__llvm_prf_cnts", "__llvm_prf_data", "__llvm_prf_name", "__llvm_prf_vnds", |
| 179 | // "__llvm_prf_vns", "__llvm_prf_vtab"}) |
| 180 | // are always live when linking on AIX, regardless if the .o's being linked |
| 181 | // reference symbols from the profile library (for example when no files were |
| 182 | // compiled with -fprofile-generate). That's because these symbols are kept |
| 183 | // alive through references in constructor functions that are always live in the |
| 184 | // default linking model on AIX (-bcdtors:all). The __start_SECNAME and |
| 185 | // __stop_SECNAME symbols are only resolved by the linker when the SECNAME |
| 186 | // section exists. So for the scenario where the user objects have no such |
| 187 | // section (i.e. when they are compiled with -fno-profile-generate), we always |
| 188 | // define these zero length variables in each of the above 4 sections. |
| 189 | static int dummy_cnts[0] COMPILER_RT_SECTION( |
| 190 | COMPILER_RT_SEG INSTR_PROF_CNTS_SECT_NAME); |
| 191 | static int dummy_bits[0] COMPILER_RT_SECTION( |
| 192 | COMPILER_RT_SEG INSTR_PROF_BITS_SECT_NAME); |
| 193 | static int dummy_data[0] COMPILER_RT_SECTION( |
| 194 | COMPILER_RT_SEG INSTR_PROF_DATA_SECT_NAME); |
| 195 | static const int dummy_name[0] COMPILER_RT_SECTION( |
| 196 | COMPILER_RT_SEG INSTR_PROF_NAME_SECT_NAME); |
| 197 | static int dummy_vnds[0] COMPILER_RT_SECTION( |
| 198 | COMPILER_RT_SEG INSTR_PROF_VNODES_SECT_NAME); |
| 199 | static int dummy_vname[0] COMPILER_RT_SECTION( |
| 200 | COMPILER_RT_SEG INSTR_PROF_VNAME_SECT_NAME); |
| 201 | static int dummy_vtab[0] COMPILER_RT_SECTION( |
| 202 | COMPILER_RT_SEG INSTR_PROF_VTAB_SECT_NAME); |
| 203 | static int dummy_covinit_funcs[0] COMPILER_RT_SECTION( |
| 204 | COMPILER_RT_SEG INSTR_PROF_COVINIT_SECT_NAME); |
| 205 | |
| 206 | // To avoid GC'ing of the dummy variables by the linker, reference them in an |
| 207 | // array and reference the array in the runtime registration code |
| 208 | // (InstrProfilingRuntime.cpp) |
| 209 | #ifdef __GNUC__ |
| 210 | #pragma GCC diagnostic push |
| 211 | #pragma GCC diagnostic ignored "-Wcast-qual" |
| 212 | #endif |
| 213 | COMPILER_RT_VISIBILITY |
| 214 | void *__llvm_profile_keep[] = { |
| 215 | (void *)&dummy_cnts, (void *)&dummy_bits, (void *)&dummy_data, |
| 216 | (void *)&dummy_name, (void *)&dummy_vnds, (void *)&dummy_vname, |
| 217 | (void *)&dummy_vtab, (void *)&dummy_covinit_funcs}; |
| 218 | #ifdef __GNUC__ |
| 219 | #pragma GCC diagnostic pop |
| 220 | #endif |
| 221 | #endif |
| 222 | |