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