xref: /llvm-project/compiler-rt/lib/profile/InstrProfilingPlatformAIX.c (revision f9d07890640434a4be0e7f651dd295478598b36d)
1787fbad5SWael Yehia /*===- InstrProfilingPlatformAIX.c - Profile data AIX platform ------------===*\
2787fbad5SWael Yehia |*
3787fbad5SWael Yehia |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4787fbad5SWael Yehia |* See https://llvm.org/LICENSE.txt for license information.
5787fbad5SWael Yehia |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6787fbad5SWael Yehia |*
7787fbad5SWael Yehia \*===----------------------------------------------------------------------===*/
8787fbad5SWael Yehia 
9787fbad5SWael Yehia #if defined(_AIX)
10787fbad5SWael Yehia 
110e2ed905SWael Yehia #ifdef __64BIT__
120e2ed905SWael Yehia #define __XCOFF64__
130e2ed905SWael Yehia #endif
140e2ed905SWael Yehia #include <errno.h>
150e2ed905SWael Yehia #include <stdlib.h>
160e2ed905SWael Yehia #include <string.h>
170e2ed905SWael Yehia #include <sys/ldr.h>
180e2ed905SWael Yehia #include <xcoff.h>
190e2ed905SWael Yehia 
20787fbad5SWael Yehia #include "InstrProfiling.h"
210e2ed905SWael Yehia #include "InstrProfilingInternal.h"
220e2ed905SWael Yehia 
230e2ed905SWael Yehia #define BIN_ID_PREFIX "xcoff_binary_id:"
240e2ed905SWael Yehia 
250e2ed905SWael Yehia // If found, write the build-id into the Result buffer.
260e2ed905SWael Yehia static size_t FindBinaryId(char *Result, size_t Size) {
270e2ed905SWael Yehia   unsigned long EntryAddr = (unsigned long)__builtin_return_address(0);
280e2ed905SWael Yehia 
290e2ed905SWael Yehia   // Use loadquery to get information about loaded modules; loadquery writes
300e2ed905SWael Yehia   // its result into a buffer of unknown size.
310e2ed905SWael Yehia   char Buf[1024];
320e2ed905SWael Yehia   size_t BufSize = sizeof(Buf);
330e2ed905SWael Yehia   char *BufPtr = Buf;
340e2ed905SWael Yehia   int RC = -1;
350e2ed905SWael Yehia 
360e2ed905SWael Yehia   errno = 0;
370e2ed905SWael Yehia   RC = loadquery(L_GETXINFO | L_IGNOREUNLOAD, BufPtr, (unsigned int)BufSize);
380e2ed905SWael Yehia   if (RC == -1 && errno == ENOMEM) {
390e2ed905SWael Yehia     BufSize = 64000; // should be plenty for any program.
400e2ed905SWael Yehia     BufPtr = malloc(BufSize);
4109ab1f33SWael Yehia     if (BufPtr != 0)
420e2ed905SWael Yehia       RC = loadquery(L_GETXINFO | L_IGNOREUNLOAD, BufPtr, (unsigned int)BufSize);
430e2ed905SWael Yehia   }
440e2ed905SWael Yehia 
450e2ed905SWael Yehia   if (RC == -1)
4609ab1f33SWael Yehia     goto done;
470e2ed905SWael Yehia 
480e2ed905SWael Yehia   // Locate the ld_xinfo corresponding to this module.
490e2ed905SWael Yehia   struct ld_xinfo *CurInfo = (struct ld_xinfo *)BufPtr;
500e2ed905SWael Yehia   while (1) {
510e2ed905SWael Yehia     unsigned long CurTextStart = (uint64_t)CurInfo->ldinfo_textorg;
520e2ed905SWael Yehia     unsigned long CurTextEnd = CurTextStart + CurInfo->ldinfo_textsize;
530e2ed905SWael Yehia     if (CurTextStart <= EntryAddr && EntryAddr < CurTextEnd) {
540e2ed905SWael Yehia       // Found my slot. Now search for the build-id.
550e2ed905SWael Yehia       char *p = (char *)CurInfo->ldinfo_textorg;
560e2ed905SWael Yehia 
570e2ed905SWael Yehia       FILHDR *f = (FILHDR *)p;
580e2ed905SWael Yehia       AOUTHDR *a = (AOUTHDR *)(p + FILHSZ);
590e2ed905SWael Yehia       SCNHDR *s =
600e2ed905SWael Yehia           (SCNHDR *)(p + FILHSZ + f->f_opthdr + SCNHSZ * (a->o_snloader - 1));
610e2ed905SWael Yehia       LDHDR *ldhdr = (LDHDR *)(p + s->s_scnptr);
620e2ed905SWael Yehia       // This is the loader string table
630e2ed905SWael Yehia       char *lstr = (char *)ldhdr + ldhdr->l_stoff;
640e2ed905SWael Yehia 
650e2ed905SWael Yehia       // If the build-id exists, it's the first entry.
660e2ed905SWael Yehia       // Each entry is comprised of a 2-byte size component, followed by the
670e2ed905SWael Yehia       // data.
680e2ed905SWael Yehia       size_t len = *(short *)lstr;
690e2ed905SWael Yehia       char *str = (char *)(lstr + 2);
700e2ed905SWael Yehia       size_t PrefixLen = sizeof(BIN_ID_PREFIX) - 1;
710e2ed905SWael Yehia       if (len > PrefixLen && (len - PrefixLen) <= Size &&
720e2ed905SWael Yehia           strncmp(str, BIN_ID_PREFIX, PrefixLen) == 0) {
730e2ed905SWael Yehia         memcpy(Result, str + PrefixLen, len - PrefixLen);
740e2ed905SWael Yehia         RC = len - PrefixLen;
750e2ed905SWael Yehia         goto done;
760e2ed905SWael Yehia       }
770e2ed905SWael Yehia       break;
780e2ed905SWael Yehia     }
790e2ed905SWael Yehia     if (CurInfo->ldinfo_next == 0u)
800e2ed905SWael Yehia       break;
810e2ed905SWael Yehia     CurInfo = (struct ld_xinfo *)((char *)CurInfo + CurInfo->ldinfo_next);
820e2ed905SWael Yehia   }
830e2ed905SWael Yehia done:
840e2ed905SWael Yehia   if (BufSize != sizeof(Buf) && BufPtr != 0)
850e2ed905SWael Yehia     free(BufPtr);
860e2ed905SWael Yehia   return RC;
870e2ed905SWael Yehia }
880e2ed905SWael Yehia 
890e2ed905SWael Yehia static int StrToHexError = 0;
900e2ed905SWael Yehia static uint8_t StrToHex(char c) {
910e2ed905SWael Yehia   if (c >= '0' && c <= '9')
920e2ed905SWael Yehia     return c - '0';
930e2ed905SWael Yehia   if (c >= 'a' && c <= 'f')
940e2ed905SWael Yehia     return c - 'a' + 0xa;
950e2ed905SWael Yehia   if (c >= 'A' && c <= 'F')
960e2ed905SWael Yehia     return c - 'A' + 0xa;
970e2ed905SWael Yehia   StrToHexError = 1;
980e2ed905SWael Yehia   return 0;
990e2ed905SWael Yehia }
1000e2ed905SWael Yehia 
1010e2ed905SWael Yehia COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) {
1020e2ed905SWael Yehia   // 200 bytes should be enough for the build-id hex string.
1030e2ed905SWael Yehia   static char Buf[200];
1040e2ed905SWael Yehia   // Profile reading tools expect this to be 8-bytes long.
1050e2ed905SWael Yehia   static int64_t BinaryIdLen = 0;
1060e2ed905SWael Yehia   static uint8_t *BinaryIdData = 0;
1070e2ed905SWael Yehia 
1080e2ed905SWael Yehia   // -1 means we already checked for a BinaryId and didn't find one.
1090e2ed905SWael Yehia   if (BinaryIdLen == -1)
1100e2ed905SWael Yehia     return 0;
1110e2ed905SWael Yehia 
1120e2ed905SWael Yehia   // Are we being called for the first time?
1130e2ed905SWael Yehia   if (BinaryIdLen == 0) {
1140e2ed905SWael Yehia     if (getenv("LLVM_PROFILE_NO_BUILD_ID"))
1150e2ed905SWael Yehia       goto fail;
1160e2ed905SWael Yehia 
1170e2ed905SWael Yehia     int BuildIdLen = FindBinaryId(Buf, sizeof(Buf));
1180e2ed905SWael Yehia     if (BuildIdLen <= 0)
1190e2ed905SWael Yehia       goto fail;
1200e2ed905SWael Yehia 
1210e2ed905SWael Yehia     if (Buf[BuildIdLen - 1] == '\0')
1220e2ed905SWael Yehia       BuildIdLen--;
1230e2ed905SWael Yehia 
1240e2ed905SWael Yehia     // assume even number of digits/chars, so 0xabc must be 0x0abc
1250e2ed905SWael Yehia     if ((BuildIdLen % 2) != 0 || BuildIdLen == 0)
1260e2ed905SWael Yehia       goto fail;
1270e2ed905SWael Yehia 
1280e2ed905SWael Yehia     // The numeric ID is represented as an ascii string in the loader section,
1290e2ed905SWael Yehia     // so convert it to raw binary.
1300e2ed905SWael Yehia     BinaryIdLen = BuildIdLen / 2;
1310e2ed905SWael Yehia     BinaryIdData = (uint8_t *)Buf;
1320e2ed905SWael Yehia 
1330e2ed905SWael Yehia     // Skip "0x" prefix if it exists.
1340e2ed905SWael Yehia     if (Buf[0] == '0' && Buf[1] == 'x') {
1350e2ed905SWael Yehia       BinaryIdLen -= 1;
1360e2ed905SWael Yehia       BinaryIdData += 2;
1370e2ed905SWael Yehia     }
1380e2ed905SWael Yehia 
1390e2ed905SWael Yehia     StrToHexError = 0;
1400e2ed905SWael Yehia     for (int i = 0; i < BinaryIdLen; i++)
1410e2ed905SWael Yehia       BinaryIdData[i] = (StrToHex(BinaryIdData[2 * i]) << 4) +
1420e2ed905SWael Yehia                         StrToHex(BinaryIdData[2 * i + 1]);
1430e2ed905SWael Yehia 
1440e2ed905SWael Yehia     if (StrToHexError)
1450e2ed905SWael Yehia       goto fail;
1460e2ed905SWael Yehia 
1470e2ed905SWael Yehia     if (getenv("LLVM_PROFILE_VERBOSE")) {
1480e2ed905SWael Yehia       char *StrBuf = (char *)COMPILER_RT_ALLOCA(2 * BinaryIdLen + 1);
1490e2ed905SWael Yehia       for (int i = 0; i < (int)BinaryIdLen; i++)
1500e2ed905SWael Yehia         sprintf(&StrBuf[2 * i], "%02x", BinaryIdData[i]);
1510e2ed905SWael Yehia       PROF_NOTE("Writing binary id: %s\n", StrBuf);
1520e2ed905SWael Yehia     }
1530e2ed905SWael Yehia   }
1540e2ed905SWael Yehia 
1550e2ed905SWael Yehia   uint8_t BinaryIdPadding = __llvm_profile_get_num_padding_bytes(BinaryIdLen);
1560e2ed905SWael Yehia   if (Writer && lprofWriteOneBinaryId(Writer, BinaryIdLen, BinaryIdData,
1570e2ed905SWael Yehia                                       BinaryIdPadding) == -1)
1580e2ed905SWael Yehia     return -1; // Return -1 rather goto fail to match the NT_GNU_BUILD_ID path.
1590e2ed905SWael Yehia 
1600e2ed905SWael Yehia   return sizeof(BinaryIdLen) + BinaryIdLen + BinaryIdPadding;
1610e2ed905SWael Yehia 
1620e2ed905SWael Yehia fail:
1630e2ed905SWael Yehia   if (getenv("LLVM_PROFILE_VERBOSE"))
1640e2ed905SWael Yehia     fprintf(stderr, "no or invalid binary id: %.*s\n", (int)sizeof(Buf), Buf);
1650e2ed905SWael Yehia   BinaryIdLen = -1;
1660e2ed905SWael Yehia   return 0;
1670e2ed905SWael Yehia }
168787fbad5SWael Yehia 
169787fbad5SWael Yehia // Empty stubs to allow linking object files using the registration-based scheme
170787fbad5SWael Yehia COMPILER_RT_VISIBILITY
171787fbad5SWael Yehia void __llvm_profile_register_function(void *Data_) {}
172787fbad5SWael Yehia 
173787fbad5SWael Yehia COMPILER_RT_VISIBILITY
174787fbad5SWael Yehia void __llvm_profile_register_names_function(void *NamesStart,
175787fbad5SWael Yehia                                             uint64_t NamesSize) {}
176787fbad5SWael Yehia 
177787fbad5SWael Yehia // The __start_SECNAME and __stop_SECNAME symbols (for SECNAME \in
17816e74fd4SMingming Liu // {"__llvm_prf_cnts", "__llvm_prf_data", "__llvm_prf_name", "__llvm_prf_vnds",
17916e74fd4SMingming Liu // "__llvm_prf_vns", "__llvm_prf_vtab"})
180787fbad5SWael Yehia // are always live when linking on AIX, regardless if the .o's being linked
181787fbad5SWael Yehia // reference symbols from the profile library (for example when no files were
182787fbad5SWael Yehia // compiled with -fprofile-generate). That's because these symbols are kept
183787fbad5SWael Yehia // alive through references in constructor functions that are always live in the
184787fbad5SWael Yehia // default linking model on AIX (-bcdtors:all). The __start_SECNAME and
185787fbad5SWael Yehia // __stop_SECNAME symbols are only resolved by the linker when the SECNAME
186787fbad5SWael Yehia // section exists. So for the scenario where the user objects have no such
187787fbad5SWael Yehia // section (i.e. when they are compiled with -fno-profile-generate), we always
188787fbad5SWael Yehia // define these zero length variables in each of the above 4 sections.
189787fbad5SWael Yehia static int dummy_cnts[0] COMPILER_RT_SECTION(
190787fbad5SWael Yehia     COMPILER_RT_SEG INSTR_PROF_CNTS_SECT_NAME);
191f95b2f1aSAlan Phipps static int dummy_bits[0] COMPILER_RT_SECTION(
192f95b2f1aSAlan Phipps     COMPILER_RT_SEG INSTR_PROF_BITS_SECT_NAME);
193787fbad5SWael Yehia static int dummy_data[0] COMPILER_RT_SECTION(
194787fbad5SWael Yehia     COMPILER_RT_SEG INSTR_PROF_DATA_SECT_NAME);
195787fbad5SWael Yehia static const int dummy_name[0] COMPILER_RT_SECTION(
196787fbad5SWael Yehia     COMPILER_RT_SEG INSTR_PROF_NAME_SECT_NAME);
197787fbad5SWael Yehia static int dummy_vnds[0] COMPILER_RT_SECTION(
198787fbad5SWael Yehia     COMPILER_RT_SEG INSTR_PROF_VNODES_SECT_NAME);
19915cccc55SWael Yehia static int dummy_orderfile[0] COMPILER_RT_SECTION(
20015cccc55SWael Yehia     COMPILER_RT_SEG INSTR_PROF_ORDERFILE_SECT_NAME);
20116e74fd4SMingming Liu static int dummy_vname[0] COMPILER_RT_SECTION(
20216e74fd4SMingming Liu     COMPILER_RT_SEG INSTR_PROF_VNAME_SECT_NAME);
20316e74fd4SMingming Liu static int dummy_vtab[0] COMPILER_RT_SECTION(
20416e74fd4SMingming Liu     COMPILER_RT_SEG INSTR_PROF_VTAB_SECT_NAME);
205*f9d07890SQiongsi Wu static int dummy_covinit_funcs[0] COMPILER_RT_SECTION(
206*f9d07890SQiongsi Wu     COMPILER_RT_SEG INSTR_PROF_COVINIT_SECT_NAME);
207787fbad5SWael Yehia 
208787fbad5SWael Yehia // To avoid GC'ing of the dummy variables by the linker, reference them in an
209787fbad5SWael Yehia // array and reference the array in the runtime registration code
210787fbad5SWael Yehia // (InstrProfilingRuntime.cpp)
2110680ca7fSJake Egan #ifdef __GNUC__
2120680ca7fSJake Egan #pragma GCC diagnostic push
2130680ca7fSJake Egan #pragma GCC diagnostic ignored "-Wcast-qual"
2140680ca7fSJake Egan #endif
215787fbad5SWael Yehia COMPILER_RT_VISIBILITY
216f95b2f1aSAlan Phipps void *__llvm_profile_keep[] = {(void *)&dummy_cnts,  (void *)&dummy_bits,
217f95b2f1aSAlan Phipps                                (void *)&dummy_data,  (void *)&dummy_name,
21816e74fd4SMingming Liu                                (void *)&dummy_vnds,  (void *)&dummy_orderfile,
219*f9d07890SQiongsi Wu                                (void *)&dummy_vname, (void *)&dummy_vtab,
220*f9d07890SQiongsi Wu                                (void *)&dummy_covinit_funcs};
2210680ca7fSJake Egan #ifdef __GNUC__
2220680ca7fSJake Egan #pragma GCC diagnostic pop
2230680ca7fSJake Egan #endif
224787fbad5SWael Yehia #endif
225