xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/profile/InstrProfilingPlatformAIX.c (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
106c3fb27SDimitry Andric /*===- InstrProfilingPlatformAIX.c - Profile data AIX platform ------------===*\
206c3fb27SDimitry Andric |*
306c3fb27SDimitry Andric |* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406c3fb27SDimitry Andric |* See https://llvm.org/LICENSE.txt for license information.
506c3fb27SDimitry Andric |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606c3fb27SDimitry Andric |*
706c3fb27SDimitry Andric \*===----------------------------------------------------------------------===*/
806c3fb27SDimitry Andric 
906c3fb27SDimitry Andric #if defined(_AIX)
1006c3fb27SDimitry Andric 
1106c3fb27SDimitry Andric #ifdef __64BIT__
1206c3fb27SDimitry Andric #define __XCOFF64__
1306c3fb27SDimitry Andric #endif
1406c3fb27SDimitry Andric #include <errno.h>
1506c3fb27SDimitry Andric #include <stdlib.h>
1606c3fb27SDimitry Andric #include <string.h>
1706c3fb27SDimitry Andric #include <sys/ldr.h>
1806c3fb27SDimitry Andric #include <xcoff.h>
1906c3fb27SDimitry Andric 
2006c3fb27SDimitry Andric #include "InstrProfiling.h"
2106c3fb27SDimitry Andric #include "InstrProfilingInternal.h"
2206c3fb27SDimitry Andric 
2306c3fb27SDimitry Andric #define BIN_ID_PREFIX "xcoff_binary_id:"
2406c3fb27SDimitry Andric 
2506c3fb27SDimitry Andric // If found, write the build-id into the Result buffer.
2606c3fb27SDimitry Andric static size_t FindBinaryId(char *Result, size_t Size) {
2706c3fb27SDimitry Andric   unsigned long EntryAddr = (unsigned long)__builtin_return_address(0);
2806c3fb27SDimitry Andric 
2906c3fb27SDimitry Andric   // Use loadquery to get information about loaded modules; loadquery writes
3006c3fb27SDimitry Andric   // its result into a buffer of unknown size.
3106c3fb27SDimitry Andric   char Buf[1024];
3206c3fb27SDimitry Andric   size_t BufSize = sizeof(Buf);
3306c3fb27SDimitry Andric   char *BufPtr = Buf;
3406c3fb27SDimitry Andric   int RC = -1;
3506c3fb27SDimitry Andric 
3606c3fb27SDimitry Andric   errno = 0;
3706c3fb27SDimitry Andric   RC = loadquery(L_GETXINFO | L_IGNOREUNLOAD, BufPtr, (unsigned int)BufSize);
3806c3fb27SDimitry Andric   if (RC == -1 && errno == ENOMEM) {
3906c3fb27SDimitry Andric     BufSize = 64000; // should be plenty for any program.
4006c3fb27SDimitry Andric     BufPtr = malloc(BufSize);
4106c3fb27SDimitry Andric     if (BufPtr != 0)
4206c3fb27SDimitry Andric       RC = loadquery(L_GETXINFO | L_IGNOREUNLOAD, BufPtr, (unsigned int)BufSize);
4306c3fb27SDimitry Andric   }
4406c3fb27SDimitry Andric 
4506c3fb27SDimitry Andric   if (RC == -1)
4606c3fb27SDimitry Andric     goto done;
4706c3fb27SDimitry Andric 
4806c3fb27SDimitry Andric   // Locate the ld_xinfo corresponding to this module.
4906c3fb27SDimitry Andric   struct ld_xinfo *CurInfo = (struct ld_xinfo *)BufPtr;
5006c3fb27SDimitry Andric   while (1) {
5106c3fb27SDimitry Andric     unsigned long CurTextStart = (uint64_t)CurInfo->ldinfo_textorg;
5206c3fb27SDimitry Andric     unsigned long CurTextEnd = CurTextStart + CurInfo->ldinfo_textsize;
5306c3fb27SDimitry Andric     if (CurTextStart <= EntryAddr && EntryAddr < CurTextEnd) {
5406c3fb27SDimitry Andric       // Found my slot. Now search for the build-id.
5506c3fb27SDimitry Andric       char *p = (char *)CurInfo->ldinfo_textorg;
5606c3fb27SDimitry Andric 
5706c3fb27SDimitry Andric       FILHDR *f = (FILHDR *)p;
5806c3fb27SDimitry Andric       AOUTHDR *a = (AOUTHDR *)(p + FILHSZ);
5906c3fb27SDimitry Andric       SCNHDR *s =
6006c3fb27SDimitry Andric           (SCNHDR *)(p + FILHSZ + f->f_opthdr + SCNHSZ * (a->o_snloader - 1));
6106c3fb27SDimitry Andric       LDHDR *ldhdr = (LDHDR *)(p + s->s_scnptr);
6206c3fb27SDimitry Andric       // This is the loader string table
6306c3fb27SDimitry Andric       char *lstr = (char *)ldhdr + ldhdr->l_stoff;
6406c3fb27SDimitry Andric 
6506c3fb27SDimitry Andric       // If the build-id exists, it's the first entry.
6606c3fb27SDimitry Andric       // Each entry is comprised of a 2-byte size component, followed by the
6706c3fb27SDimitry Andric       // data.
6806c3fb27SDimitry Andric       size_t len = *(short *)lstr;
6906c3fb27SDimitry Andric       char *str = (char *)(lstr + 2);
7006c3fb27SDimitry Andric       size_t PrefixLen = sizeof(BIN_ID_PREFIX) - 1;
7106c3fb27SDimitry Andric       if (len > PrefixLen && (len - PrefixLen) <= Size &&
7206c3fb27SDimitry Andric           strncmp(str, BIN_ID_PREFIX, PrefixLen) == 0) {
7306c3fb27SDimitry Andric         memcpy(Result, str + PrefixLen, len - PrefixLen);
7406c3fb27SDimitry Andric         RC = len - PrefixLen;
7506c3fb27SDimitry Andric         goto done;
7606c3fb27SDimitry Andric       }
7706c3fb27SDimitry Andric       break;
7806c3fb27SDimitry Andric     }
7906c3fb27SDimitry Andric     if (CurInfo->ldinfo_next == 0u)
8006c3fb27SDimitry Andric       break;
8106c3fb27SDimitry Andric     CurInfo = (struct ld_xinfo *)((char *)CurInfo + CurInfo->ldinfo_next);
8206c3fb27SDimitry Andric   }
8306c3fb27SDimitry Andric done:
8406c3fb27SDimitry Andric   if (BufSize != sizeof(Buf) && BufPtr != 0)
8506c3fb27SDimitry Andric     free(BufPtr);
8606c3fb27SDimitry Andric   return RC;
8706c3fb27SDimitry Andric }
8806c3fb27SDimitry Andric 
8906c3fb27SDimitry Andric static int StrToHexError = 0;
9006c3fb27SDimitry Andric static uint8_t StrToHex(char c) {
9106c3fb27SDimitry Andric   if (c >= '0' && c <= '9')
9206c3fb27SDimitry Andric     return c - '0';
9306c3fb27SDimitry Andric   if (c >= 'a' && c <= 'f')
9406c3fb27SDimitry Andric     return c - 'a' + 0xa;
9506c3fb27SDimitry Andric   if (c >= 'A' && c <= 'F')
9606c3fb27SDimitry Andric     return c - 'A' + 0xa;
9706c3fb27SDimitry Andric   StrToHexError = 1;
9806c3fb27SDimitry Andric   return 0;
9906c3fb27SDimitry Andric }
10006c3fb27SDimitry Andric 
10106c3fb27SDimitry Andric COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) {
10206c3fb27SDimitry Andric   // 200 bytes should be enough for the build-id hex string.
10306c3fb27SDimitry Andric   static char Buf[200];
10406c3fb27SDimitry Andric   // Profile reading tools expect this to be 8-bytes long.
10506c3fb27SDimitry Andric   static int64_t BinaryIdLen = 0;
10606c3fb27SDimitry Andric   static uint8_t *BinaryIdData = 0;
10706c3fb27SDimitry Andric 
10806c3fb27SDimitry Andric   // -1 means we already checked for a BinaryId and didn't find one.
10906c3fb27SDimitry Andric   if (BinaryIdLen == -1)
11006c3fb27SDimitry Andric     return 0;
11106c3fb27SDimitry Andric 
11206c3fb27SDimitry Andric   // Are we being called for the first time?
11306c3fb27SDimitry Andric   if (BinaryIdLen == 0) {
11406c3fb27SDimitry Andric     if (getenv("LLVM_PROFILE_NO_BUILD_ID"))
11506c3fb27SDimitry Andric       goto fail;
11606c3fb27SDimitry Andric 
11706c3fb27SDimitry Andric     int BuildIdLen = FindBinaryId(Buf, sizeof(Buf));
11806c3fb27SDimitry Andric     if (BuildIdLen <= 0)
11906c3fb27SDimitry Andric       goto fail;
12006c3fb27SDimitry Andric 
12106c3fb27SDimitry Andric     if (Buf[BuildIdLen - 1] == '\0')
12206c3fb27SDimitry Andric       BuildIdLen--;
12306c3fb27SDimitry Andric 
12406c3fb27SDimitry Andric     // assume even number of digits/chars, so 0xabc must be 0x0abc
12506c3fb27SDimitry Andric     if ((BuildIdLen % 2) != 0 || BuildIdLen == 0)
12606c3fb27SDimitry Andric       goto fail;
12706c3fb27SDimitry Andric 
12806c3fb27SDimitry Andric     // The numeric ID is represented as an ascii string in the loader section,
12906c3fb27SDimitry Andric     // so convert it to raw binary.
13006c3fb27SDimitry Andric     BinaryIdLen = BuildIdLen / 2;
13106c3fb27SDimitry Andric     BinaryIdData = (uint8_t *)Buf;
13206c3fb27SDimitry Andric 
13306c3fb27SDimitry Andric     // Skip "0x" prefix if it exists.
13406c3fb27SDimitry Andric     if (Buf[0] == '0' && Buf[1] == 'x') {
13506c3fb27SDimitry Andric       BinaryIdLen -= 1;
13606c3fb27SDimitry Andric       BinaryIdData += 2;
13706c3fb27SDimitry Andric     }
13806c3fb27SDimitry Andric 
13906c3fb27SDimitry Andric     StrToHexError = 0;
14006c3fb27SDimitry Andric     for (int i = 0; i < BinaryIdLen; i++)
14106c3fb27SDimitry Andric       BinaryIdData[i] = (StrToHex(BinaryIdData[2 * i]) << 4) +
14206c3fb27SDimitry Andric                         StrToHex(BinaryIdData[2 * i + 1]);
14306c3fb27SDimitry Andric 
14406c3fb27SDimitry Andric     if (StrToHexError)
14506c3fb27SDimitry Andric       goto fail;
14606c3fb27SDimitry Andric 
14706c3fb27SDimitry Andric     if (getenv("LLVM_PROFILE_VERBOSE")) {
14806c3fb27SDimitry Andric       char *StrBuf = (char *)COMPILER_RT_ALLOCA(2 * BinaryIdLen + 1);
14906c3fb27SDimitry Andric       for (int i = 0; i < (int)BinaryIdLen; i++)
15006c3fb27SDimitry Andric         sprintf(&StrBuf[2 * i], "%02x", BinaryIdData[i]);
15106c3fb27SDimitry Andric       PROF_NOTE("Writing binary id: %s\n", StrBuf);
15206c3fb27SDimitry Andric     }
15306c3fb27SDimitry Andric   }
15406c3fb27SDimitry Andric 
15506c3fb27SDimitry Andric   uint8_t BinaryIdPadding = __llvm_profile_get_num_padding_bytes(BinaryIdLen);
15606c3fb27SDimitry Andric   if (Writer && lprofWriteOneBinaryId(Writer, BinaryIdLen, BinaryIdData,
15706c3fb27SDimitry Andric                                       BinaryIdPadding) == -1)
15806c3fb27SDimitry Andric     return -1; // Return -1 rather goto fail to match the NT_GNU_BUILD_ID path.
15906c3fb27SDimitry Andric 
16006c3fb27SDimitry Andric   return sizeof(BinaryIdLen) + BinaryIdLen + BinaryIdPadding;
16106c3fb27SDimitry Andric 
16206c3fb27SDimitry Andric fail:
16306c3fb27SDimitry Andric   if (getenv("LLVM_PROFILE_VERBOSE"))
16406c3fb27SDimitry Andric     fprintf(stderr, "no or invalid binary id: %.*s\n", (int)sizeof(Buf), Buf);
16506c3fb27SDimitry Andric   BinaryIdLen = -1;
16606c3fb27SDimitry Andric   return 0;
16706c3fb27SDimitry Andric }
16806c3fb27SDimitry Andric 
16906c3fb27SDimitry Andric // Empty stubs to allow linking object files using the registration-based scheme
17006c3fb27SDimitry Andric COMPILER_RT_VISIBILITY
17106c3fb27SDimitry Andric void __llvm_profile_register_function(void *Data_) {}
17206c3fb27SDimitry Andric 
17306c3fb27SDimitry Andric COMPILER_RT_VISIBILITY
17406c3fb27SDimitry Andric void __llvm_profile_register_names_function(void *NamesStart,
17506c3fb27SDimitry Andric                                             uint64_t NamesSize) {}
17606c3fb27SDimitry Andric 
17706c3fb27SDimitry Andric // The __start_SECNAME and __stop_SECNAME symbols (for SECNAME \in
178*0fca6ea1SDimitry Andric // {"__llvm_prf_cnts", "__llvm_prf_data", "__llvm_prf_name", "__llvm_prf_vnds",
179*0fca6ea1SDimitry Andric // "__llvm_prf_vns", "__llvm_prf_vtab"})
18006c3fb27SDimitry Andric // are always live when linking on AIX, regardless if the .o's being linked
18106c3fb27SDimitry Andric // reference symbols from the profile library (for example when no files were
18206c3fb27SDimitry Andric // compiled with -fprofile-generate). That's because these symbols are kept
18306c3fb27SDimitry Andric // alive through references in constructor functions that are always live in the
18406c3fb27SDimitry Andric // default linking model on AIX (-bcdtors:all). The __start_SECNAME and
18506c3fb27SDimitry Andric // __stop_SECNAME symbols are only resolved by the linker when the SECNAME
18606c3fb27SDimitry Andric // section exists. So for the scenario where the user objects have no such
18706c3fb27SDimitry Andric // section (i.e. when they are compiled with -fno-profile-generate), we always
18806c3fb27SDimitry Andric // define these zero length variables in each of the above 4 sections.
18906c3fb27SDimitry Andric static int dummy_cnts[0] COMPILER_RT_SECTION(
19006c3fb27SDimitry Andric     COMPILER_RT_SEG INSTR_PROF_CNTS_SECT_NAME);
1915f757f3fSDimitry Andric static int dummy_bits[0] COMPILER_RT_SECTION(
1925f757f3fSDimitry Andric     COMPILER_RT_SEG INSTR_PROF_BITS_SECT_NAME);
19306c3fb27SDimitry Andric static int dummy_data[0] COMPILER_RT_SECTION(
19406c3fb27SDimitry Andric     COMPILER_RT_SEG INSTR_PROF_DATA_SECT_NAME);
19506c3fb27SDimitry Andric static const int dummy_name[0] COMPILER_RT_SECTION(
19606c3fb27SDimitry Andric     COMPILER_RT_SEG INSTR_PROF_NAME_SECT_NAME);
19706c3fb27SDimitry Andric static int dummy_vnds[0] COMPILER_RT_SECTION(
19806c3fb27SDimitry Andric     COMPILER_RT_SEG INSTR_PROF_VNODES_SECT_NAME);
19974626c16SDimitry Andric static int dummy_orderfile[0] COMPILER_RT_SECTION(
20074626c16SDimitry Andric     COMPILER_RT_SEG INSTR_PROF_ORDERFILE_SECT_NAME);
201*0fca6ea1SDimitry Andric static int dummy_vname[0] COMPILER_RT_SECTION(
202*0fca6ea1SDimitry Andric     COMPILER_RT_SEG INSTR_PROF_VNAME_SECT_NAME);
203*0fca6ea1SDimitry Andric static int dummy_vtab[0] COMPILER_RT_SECTION(
204*0fca6ea1SDimitry Andric     COMPILER_RT_SEG INSTR_PROF_VTAB_SECT_NAME);
20506c3fb27SDimitry Andric 
20606c3fb27SDimitry Andric // To avoid GC'ing of the dummy variables by the linker, reference them in an
20706c3fb27SDimitry Andric // array and reference the array in the runtime registration code
20806c3fb27SDimitry Andric // (InstrProfilingRuntime.cpp)
20906c3fb27SDimitry Andric #ifdef __GNUC__
21006c3fb27SDimitry Andric #pragma GCC diagnostic push
21106c3fb27SDimitry Andric #pragma GCC diagnostic ignored "-Wcast-qual"
21206c3fb27SDimitry Andric #endif
21306c3fb27SDimitry Andric COMPILER_RT_VISIBILITY
2145f757f3fSDimitry Andric void *__llvm_profile_keep[] = {(void *)&dummy_cnts,  (void *)&dummy_bits,
2155f757f3fSDimitry Andric                                (void *)&dummy_data,  (void *)&dummy_name,
216*0fca6ea1SDimitry Andric                                (void *)&dummy_vnds,  (void *)&dummy_orderfile,
217*0fca6ea1SDimitry Andric                                (void *)&dummy_vname, (void *)&dummy_vtab};
21806c3fb27SDimitry Andric #ifdef __GNUC__
21906c3fb27SDimitry Andric #pragma GCC diagnostic pop
22006c3fb27SDimitry Andric #endif
22106c3fb27SDimitry Andric #endif
222