1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
9 #include <linux/objtool_types.h>
10 #include <asm/orc_types.h>
12 #include <objtool/check.h>
13 #include <objtool/orc.h>
14 #include <objtool/warn.h>
15 #include <objtool/endianness.h>
17 struct orc_list_entry
{
18 struct list_head list
;
20 struct section
*insn_sec
;
21 unsigned long insn_off
;
24 static int orc_list_add(struct list_head
*orc_list
, struct orc_entry
*orc
,
25 struct section
*sec
, unsigned long offset
)
27 struct orc_list_entry
*entry
= malloc(sizeof(*entry
));
30 WARN("malloc failed");
35 entry
->insn_sec
= sec
;
36 entry
->insn_off
= offset
;
38 list_add_tail(&entry
->list
, orc_list
);
42 static unsigned long alt_group_len(struct alt_group
*alt_group
)
44 return alt_group
->last_insn
->offset
+
45 alt_group
->last_insn
->len
-
46 alt_group
->first_insn
->offset
;
49 int orc_create(struct objtool_file
*file
)
51 struct section
*sec
, *orc_sec
;
52 unsigned int nr
= 0, idx
= 0;
53 struct orc_list_entry
*entry
;
54 struct list_head orc_list
;
56 struct orc_entry null
= { .type
= ORC_TYPE_UNDEFINED
};
58 /* Build a deduplicated list of ORC entries: */
59 INIT_LIST_HEAD(&orc_list
);
60 for_each_sec(file
, sec
) {
61 struct orc_entry orc
, prev_orc
= {0};
62 struct instruction
*insn
;
68 sec_for_each_insn(file
, sec
, insn
) {
69 struct alt_group
*alt_group
= insn
->alt_group
;
73 if (init_orc_entry(&orc
, insn
->cfi
, insn
))
75 if (!memcmp(&prev_orc
, &orc
, sizeof(orc
)))
77 if (orc_list_add(&orc_list
, &orc
, sec
,
87 * Alternatives can have different stack layout
88 * possibilities (but they shouldn't conflict).
89 * Instead of traversing the instructions, use the
90 * alt_group's flattened byte-offset-addressed CFI
93 for (i
= 0; i
< alt_group_len(alt_group
); i
++) {
94 struct cfi_state
*cfi
= alt_group
->cfi
[i
];
97 /* errors are reported on the original insn */
98 if (init_orc_entry(&orc
, cfi
, insn
))
100 if (!memcmp(&prev_orc
, &orc
, sizeof(orc
)))
102 if (orc_list_add(&orc_list
, &orc
, insn
->sec
,
110 /* Skip to the end of the alt_group */
111 insn
= alt_group
->last_insn
;
114 /* Add a section terminator */
116 orc_list_add(&orc_list
, &null
, sec
, sec
->sh
.sh_size
);
123 /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */
124 sec
= find_section_by_name(file
->elf
, ".orc_unwind");
126 WARN("file already has .orc_unwind section, skipping");
129 orc_sec
= elf_create_section(file
->elf
, ".orc_unwind",
130 sizeof(struct orc_entry
), nr
);
134 sec
= elf_create_section_pair(file
->elf
, ".orc_unwind_ip", sizeof(int), nr
, nr
);
138 /* Write ORC entries to sections: */
139 list_for_each_entry(entry
, &orc_list
, list
) {
140 if (write_orc_entry(file
->elf
, orc_sec
, sec
, idx
++,
141 entry
->insn_sec
, entry
->insn_off
,