1 /* SPDX-License-Identifier: GPL-2.0-only */
5 * Added ORC unwind tables sort support and other updates:
6 * Copyright (C) 1999-2019 Alibaba Group Holding Limited. by:
7 * Shile Zhang <shile.zhang@linux.alibaba.com>
9 * Copyright 2011 - 2012 Cavium, Inc.
11 * Some of code was taken out of arch/x86/kernel/unwind_orc.c, written by:
12 * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
14 * Some of this code was taken out of recordmcount.h written by:
16 * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>. All rights reserved.
17 * Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
20 #undef extable_ent_size
21 #undef compare_extable
23 #undef sort_mcount_loc
45 # define extable_ent_size 16
46 # define compare_extable compare_extable_64
47 # define get_mcount_loc get_mcount_loc_64
48 # define sort_mcount_loc sort_mcount_loc_64
49 # define elf_mcount_loc elf_mcount_loc_64
50 # define do_sort do_sort_64
51 # define Elf_Addr Elf64_Addr
52 # define Elf_Ehdr Elf64_Ehdr
53 # define Elf_Shdr Elf64_Shdr
54 # define Elf_Rel Elf64_Rel
55 # define Elf_Rela Elf64_Rela
56 # define Elf_Sym Elf64_Sym
57 # define ELF_R_SYM ELF64_R_SYM
58 # define Elf_r_sym Elf64_r_sym
59 # define ELF_R_INFO ELF64_R_INFO
60 # define Elf_r_info Elf64_r_info
61 # define ELF_ST_BIND ELF64_ST_BIND
62 # define ELF_ST_TYPE ELF64_ST_TYPE
63 # define fn_ELF_R_SYM fn_ELF64_R_SYM
64 # define fn_ELF_R_INFO fn_ELF64_R_INFO
65 # define uint_t uint64_t
69 # define extable_ent_size 8
70 # define compare_extable compare_extable_32
71 # define get_mcount_loc get_mcount_loc_32
72 # define sort_mcount_loc sort_mcount_loc_32
73 # define elf_mcount_loc elf_mcount_loc_32
74 # define do_sort do_sort_32
75 # define Elf_Addr Elf32_Addr
76 # define Elf_Ehdr Elf32_Ehdr
77 # define Elf_Shdr Elf32_Shdr
78 # define Elf_Rel Elf32_Rel
79 # define Elf_Rela Elf32_Rela
80 # define Elf_Sym Elf32_Sym
81 # define ELF_R_SYM ELF32_R_SYM
82 # define Elf_r_sym Elf32_r_sym
83 # define ELF_R_INFO ELF32_R_INFO
84 # define Elf_r_info Elf32_r_info
85 # define ELF_ST_BIND ELF32_ST_BIND
86 # define ELF_ST_TYPE ELF32_ST_TYPE
87 # define fn_ELF_R_SYM fn_ELF32_R_SYM
88 # define fn_ELF_R_INFO fn_ELF32_R_INFO
89 # define uint_t uint32_t
94 #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
95 /* ORC unwinder only support X86_64 */
96 #include <asm/orc_types.h>
98 #define ERRSTR_MAXSZ 256
100 char g_err
[ERRSTR_MAXSZ
];
102 struct orc_entry
*g_orc_table
;
104 pthread_t orc_sort_thread
;
106 static inline unsigned long orc_ip(const int *ip
)
108 return (unsigned long)ip
+ *ip
;
111 static int orc_sort_cmp(const void *_a
, const void *_b
)
113 struct orc_entry
*orc_a
;
114 const int *a
= g_orc_ip_table
+ *(int *)_a
;
115 const int *b
= g_orc_ip_table
+ *(int *)_b
;
116 unsigned long a_val
= orc_ip(a
);
117 unsigned long b_val
= orc_ip(b
);
125 * The "weak" section terminator entries need to always be on the left
126 * to ensure the lookup code skips them in favor of real entries.
127 * These terminator entries exist to handle any gaps created by
128 * whitelisted .o files which didn't get objtool generation.
130 orc_a
= g_orc_table
+ (a
- g_orc_ip_table
);
131 return orc_a
->type
== ORC_TYPE_UNDEFINED
? -1 : 1;
134 static void *sort_orctable(void *arg
)
138 int *tmp_orc_ip_table
= NULL
;
139 struct orc_entry
*tmp_orc_table
= NULL
;
140 unsigned int *orc_ip_size
= (unsigned int *)arg
;
141 unsigned int num_entries
= *orc_ip_size
/ sizeof(int);
142 unsigned int orc_size
= num_entries
* sizeof(struct orc_entry
);
144 idxs
= (int *)malloc(*orc_ip_size
);
146 snprintf(g_err
, ERRSTR_MAXSZ
, "malloc idxs: %s",
151 tmp_orc_ip_table
= (int *)malloc(*orc_ip_size
);
152 if (!tmp_orc_ip_table
) {
153 snprintf(g_err
, ERRSTR_MAXSZ
, "malloc tmp_orc_ip_table: %s",
158 tmp_orc_table
= (struct orc_entry
*)malloc(orc_size
);
159 if (!tmp_orc_table
) {
160 snprintf(g_err
, ERRSTR_MAXSZ
, "malloc tmp_orc_table: %s",
165 /* initialize indices array, convert ip_table to absolute address */
166 for (i
= 0; i
< num_entries
; i
++) {
168 tmp_orc_ip_table
[i
] = g_orc_ip_table
[i
] + i
* sizeof(int);
170 memcpy(tmp_orc_table
, g_orc_table
, orc_size
);
172 qsort(idxs
, num_entries
, sizeof(int), orc_sort_cmp
);
174 for (i
= 0; i
< num_entries
; i
++) {
178 /* convert back to relative address */
179 g_orc_ip_table
[i
] = tmp_orc_ip_table
[idxs
[i
]] - i
* sizeof(int);
180 g_orc_table
[i
] = tmp_orc_table
[idxs
[i
]];
184 free(tmp_orc_ip_table
);
190 static int compare_extable(const void *a
, const void *b
)
201 #ifdef MCOUNT_SORT_ENABLED
202 pthread_t mcount_sort_thread
;
204 struct elf_mcount_loc
{
206 Elf_Shdr
*init_data_sec
;
207 uint_t start_mcount_loc
;
208 uint_t stop_mcount_loc
;
211 /* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */
212 static void *sort_mcount_loc(void *arg
)
214 struct elf_mcount_loc
*emloc
= (struct elf_mcount_loc
*)arg
;
215 uint_t offset
= emloc
->start_mcount_loc
- _r(&(emloc
->init_data_sec
)->sh_addr
)
216 + _r(&(emloc
->init_data_sec
)->sh_offset
);
217 uint_t count
= emloc
->stop_mcount_loc
- emloc
->start_mcount_loc
;
218 unsigned char *start_loc
= (void *)emloc
->ehdr
+ offset
;
220 qsort(start_loc
, count
/sizeof(uint_t
), sizeof(uint_t
), compare_extable
);
224 /* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */
225 static void get_mcount_loc(uint_t
*_start
, uint_t
*_stop
)
227 FILE *file_start
, *file_stop
;
232 file_start
= popen(" grep start_mcount System.map | awk '{print $1}' ", "r");
234 fprintf(stderr
, "get start_mcount_loc error!");
238 file_stop
= popen(" grep stop_mcount System.map | awk '{print $1}' ", "r");
240 fprintf(stderr
, "get stop_mcount_loc error!");
245 while (fgets(start_buff
, sizeof(start_buff
), file_start
) != NULL
) {
246 len
= strlen(start_buff
);
247 start_buff
[len
- 1] = '\0';
249 *_start
= strtoul(start_buff
, NULL
, 16);
251 while (fgets(stop_buff
, sizeof(stop_buff
), file_stop
) != NULL
) {
252 len
= strlen(stop_buff
);
253 stop_buff
[len
- 1] = '\0';
255 *_stop
= strtoul(stop_buff
, NULL
, 16);
261 static int do_sort(Elf_Ehdr
*ehdr
,
262 char const *const fname
,
263 table_sort_t custom_sort
)
266 Elf_Shdr
*s
, *shdr
= (Elf_Shdr
*)((char *)ehdr
+ _r(&ehdr
->e_shoff
));
267 Elf_Shdr
*strtab_sec
= NULL
;
268 Elf_Shdr
*symtab_sec
= NULL
;
269 Elf_Shdr
*extab_sec
= NULL
;
271 const Elf_Sym
*symtab
;
272 Elf32_Word
*symtab_shndx
= NULL
;
273 Elf_Sym
*sort_needed_sym
= NULL
;
274 Elf_Shdr
*sort_needed_sec
;
275 Elf_Rel
*relocs
= NULL
;
277 uint32_t *sort_needed_loc
;
278 const char *secstrings
;
285 unsigned int shstrndx
;
286 #ifdef MCOUNT_SORT_ENABLED
287 struct elf_mcount_loc mstruct
= {0};
288 uint_t _start_mcount_loc
= 0;
289 uint_t _stop_mcount_loc
= 0;
291 #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
292 unsigned int orc_ip_size
= 0;
293 unsigned int orc_size
= 0;
294 unsigned int orc_num_entries
= 0;
297 shstrndx
= r2(&ehdr
->e_shstrndx
);
298 if (shstrndx
== SHN_XINDEX
)
299 shstrndx
= r(&shdr
[0].sh_link
);
300 secstrings
= (const char *)ehdr
+ _r(&shdr
[shstrndx
].sh_offset
);
302 shnum
= r2(&ehdr
->e_shnum
);
303 if (shnum
== SHN_UNDEF
)
304 shnum
= _r(&shdr
[0].sh_size
);
306 for (i
= 0, s
= shdr
; s
< shdr
+ shnum
; i
++, s
++) {
307 idx
= r(&s
->sh_name
);
308 if (!strcmp(secstrings
+ idx
, "__ex_table")) {
312 if (!strcmp(secstrings
+ idx
, ".symtab"))
314 if (!strcmp(secstrings
+ idx
, ".strtab"))
317 if ((r(&s
->sh_type
) == SHT_REL
||
318 r(&s
->sh_type
) == SHT_RELA
) &&
319 r(&s
->sh_info
) == extab_index
) {
320 relocs
= (void *)ehdr
+ _r(&s
->sh_offset
);
321 relocs_size
= _r(&s
->sh_size
);
323 if (r(&s
->sh_type
) == SHT_SYMTAB_SHNDX
)
324 symtab_shndx
= (Elf32_Word
*)((const char *)ehdr
+
327 #ifdef MCOUNT_SORT_ENABLED
328 /* locate the .init.data section in vmlinux */
329 if (!strcmp(secstrings
+ idx
, ".init.data")) {
330 get_mcount_loc(&_start_mcount_loc
, &_stop_mcount_loc
);
332 mstruct
.init_data_sec
= s
;
333 mstruct
.start_mcount_loc
= _start_mcount_loc
;
334 mstruct
.stop_mcount_loc
= _stop_mcount_loc
;
338 #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
339 /* locate the ORC unwind tables */
340 if (!strcmp(secstrings
+ idx
, ".orc_unwind_ip")) {
341 orc_ip_size
= s
->sh_size
;
342 g_orc_ip_table
= (int *)((void *)ehdr
+
345 if (!strcmp(secstrings
+ idx
, ".orc_unwind")) {
346 orc_size
= s
->sh_size
;
347 g_orc_table
= (struct orc_entry
*)((void *)ehdr
+
353 #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
354 if (!g_orc_ip_table
|| !g_orc_table
) {
356 "incomplete ORC unwind tables in file: %s\n", fname
);
360 orc_num_entries
= orc_ip_size
/ sizeof(int);
361 if (orc_ip_size
% sizeof(int) != 0 ||
362 orc_size
% sizeof(struct orc_entry
) != 0 ||
363 orc_num_entries
!= orc_size
/ sizeof(struct orc_entry
)) {
365 "inconsistent ORC unwind table entries in file: %s\n",
370 /* create thread to sort ORC unwind tables concurrently */
371 if (pthread_create(&orc_sort_thread
, NULL
,
372 sort_orctable
, &orc_ip_size
)) {
374 "pthread_create orc_sort_thread failed '%s': %s\n",
375 strerror(errno
), fname
);
380 #ifdef MCOUNT_SORT_ENABLED
381 if (!mstruct
.init_data_sec
|| !_start_mcount_loc
|| !_stop_mcount_loc
) {
383 "incomplete mcount's sort in file: %s\n",
388 /* create thread to sort mcount_loc concurrently */
389 if (pthread_create(&mcount_sort_thread
, NULL
, &sort_mcount_loc
, &mstruct
)) {
391 "pthread_create mcount_sort_thread failed '%s': %s\n",
392 strerror(errno
), fname
);
397 fprintf(stderr
, "no __ex_table in file: %s\n", fname
);
402 fprintf(stderr
, "no .symtab in file: %s\n", fname
);
407 fprintf(stderr
, "no .strtab in file: %s\n", fname
);
411 extab_image
= (void *)ehdr
+ _r(&extab_sec
->sh_offset
);
412 strtab
= (const char *)ehdr
+ _r(&strtab_sec
->sh_offset
);
413 symtab
= (const Elf_Sym
*)((const char *)ehdr
+
414 _r(&symtab_sec
->sh_offset
));
417 custom_sort(extab_image
, _r(&extab_sec
->sh_size
));
419 int num_entries
= _r(&extab_sec
->sh_size
) / extable_ent_size
;
420 qsort(extab_image
, num_entries
,
421 extable_ent_size
, compare_extable
);
424 /* If there were relocations, we no longer need them. */
426 memset(relocs
, 0, relocs_size
);
428 /* find the flag main_extable_sort_needed */
429 for (sym
= (void *)ehdr
+ _r(&symtab_sec
->sh_offset
);
430 sym
< sym
+ _r(&symtab_sec
->sh_size
) / sizeof(Elf_Sym
);
432 if (ELF_ST_TYPE(sym
->st_info
) != STT_OBJECT
)
434 if (!strcmp(strtab
+ r(&sym
->st_name
),
435 "main_extable_sort_needed")) {
436 sort_needed_sym
= sym
;
441 if (!sort_needed_sym
) {
443 "no main_extable_sort_needed symbol in file: %s\n",
448 sort_needed_sec
= &shdr
[get_secindex(r2(&sym
->st_shndx
),
449 sort_needed_sym
- symtab
,
451 sort_needed_loc
= (void *)ehdr
+
452 _r(&sort_needed_sec
->sh_offset
) +
453 _r(&sort_needed_sym
->st_value
) -
454 _r(&sort_needed_sec
->sh_addr
);
456 /* extable has been sorted, clear the flag */
457 w(0, sort_needed_loc
);
461 #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED)
462 if (orc_sort_thread
) {
464 /* wait for ORC tables sort done */
465 rc
= pthread_join(orc_sort_thread
, &retval
);
468 "pthread_join failed '%s': %s\n",
469 strerror(errno
), fname
);
473 "failed to sort ORC tables '%s': %s\n",
474 (char *)retval
, fname
);
479 #ifdef MCOUNT_SORT_ENABLED
480 if (mcount_sort_thread
) {
482 /* wait for mcount sort done */
483 rc
= pthread_join(mcount_sort_thread
, &retval
);
486 "pthread_join failed '%s': %s\n",
487 strerror(errno
), fname
);
491 "failed to sort mcount '%s': %s\n",
492 (char *)retval
, fname
);