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 // are always live when linking on AIX, regardless if the .o's being linked
180 // reference symbols from the profile library (for example when no files were
181 // compiled with -fprofile-generate). That's because these symbols are kept
182 // alive through references in constructor functions that are always live in the
183 // default linking model on AIX (-bcdtors:all). The __start_SECNAME and
184 // __stop_SECNAME symbols are only resolved by the linker when the SECNAME
185 // section exists. So for the scenario where the user objects have no such
186 // section (i.e. when they are compiled with -fno-profile-generate), we always
187 // define these zero length variables in each of the above 4 sections.
188 static int dummy_cnts[0] COMPILER_RT_SECTION(
189     COMPILER_RT_SEG INSTR_PROF_CNTS_SECT_NAME);
190 static int dummy_data[0] COMPILER_RT_SECTION(
191     COMPILER_RT_SEG INSTR_PROF_DATA_SECT_NAME);
192 static const int dummy_name[0] COMPILER_RT_SECTION(
193     COMPILER_RT_SEG INSTR_PROF_NAME_SECT_NAME);
194 static int dummy_vnds[0] COMPILER_RT_SECTION(
195     COMPILER_RT_SEG INSTR_PROF_VNODES_SECT_NAME);
196 
197 // To avoid GC'ing of the dummy variables by the linker, reference them in an
198 // array and reference the array in the runtime registration code
199 // (InstrProfilingRuntime.cpp)
200 #ifdef __GNUC__
201 #pragma GCC diagnostic push
202 #pragma GCC diagnostic ignored "-Wcast-qual"
203 #endif
204 COMPILER_RT_VISIBILITY
205 void *__llvm_profile_keep[] = {(void *)&dummy_cnts, (void *)&dummy_data,
206                                (void *)&dummy_name, (void *)&dummy_vnds};
207 #ifdef __GNUC__
208 #pragma GCC diagnostic pop
209 #endif
210 #endif
211