libipt, insn: use insn and insn_ext to proceed without trace
Instead of using struct pt_ild directly in pt_insn_next_ip(), we use pt_insn
and pt_insn_ext objects passed as arguments. Move the function to pt_insn.c.
In pt_ip_is_ahead() we initialize respective pt_insn and pt_insn_ext objects
similar to decode_insn(). This temporarily adds code for populating those
objects until later patches will move this into the ILD.
Change-Id: Ice1efc72ad2d900a6da1eedda4cdcd71ac030db4
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
diff --git a/libipt/internal/include/pt_insn.h b/libipt/internal/include/pt_insn.h
index 177326a..b470972 100644
--- a/libipt/internal/include/pt_insn.h
+++ b/libipt/internal/include/pt_insn.h
@@ -147,4 +147,16 @@
extern int pt_insn_binds_to_vmcs(const struct pt_insn *insn,
const struct pt_insn_ext *iext);
+/* Determine the IP of the next instruction.
+ *
+ * Tries to determine the IP of the next instruction without using trace and
+ * provides it in @ip unless @ip is NULL.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ * Returns -pte_bad_query if the IP can't be determined.
+ * Returns -pte_internal if @insn or @iext is NULL.
+ */
+extern int pt_insn_next_ip(uint64_t *ip, const struct pt_insn *insn,
+ const struct pt_insn_ext *iext);
+
#endif /* PT_INSN_H */
diff --git a/libipt/src/pt_insn.c b/libipt/src/pt_insn.c
index 1989d6c..81e7e24 100644
--- a/libipt/src/pt_insn.c
+++ b/libipt/src/pt_insn.c
@@ -125,3 +125,32 @@
return 1;
}
}
+
+int pt_insn_next_ip(uint64_t *ip, const struct pt_insn *insn,
+ const struct pt_insn_ext *iext)
+{
+ if (!insn || !iext)
+ return -pte_internal;
+
+ switch (insn->iclass) {
+ case ptic_other:
+ if (ip)
+ *ip = insn->ip + insn->size;
+ return 0;
+
+ case ptic_call:
+ case ptic_jump:
+ if (iext->variant.branch.is_direct) {
+ if (ip)
+ *ip = iext->variant.branch.target;
+ return 0;
+ }
+
+ /* Fall through. */
+ default:
+ return -pte_bad_query;
+
+ case ptic_error:
+ return -pte_bad_insn;
+ }
+}
diff --git a/libipt/src/pt_insn_decoder.c b/libipt/src/pt_insn_decoder.c
index 9321d75..0273cf8 100644
--- a/libipt/src/pt_insn_decoder.c
+++ b/libipt/src/pt_insn_decoder.c
@@ -249,39 +249,6 @@
return ild->u.s.branch_far ? ptic_far_jump : ptic_jump;
}
-/* Try to determine the next IP for @ild without using Intel PT.
- *
- * If @ip is not NULL, provides the determined IP on success.
- *
- * Returns 0 on success.
- * Returns a negative error code, otherwise.
- * Returns -pte_bad_query if determining the IP would require Intel PT.
- * Returns -pte_bad_insn if @ild has not been decoded correctly.
- * Returns -pte_invalid if @ild is NULL.
- */
-static int pt_insn_next_ip(uint64_t *ip, const struct pt_ild *ild)
-{
- if (!ild)
- return -pte_invalid;
-
- if (!ild->u.s.branch) {
- if (ip)
- *ip = ild->runtime_address + ild->length;
- return 0;
- }
-
- if (ild->u.s.cond)
- return -pte_bad_query;
-
- if (ild->u.s.branch_direct) {
- if (ip)
- *ip = ild->direct_target;
- return 0;
- }
-
- return -pte_bad_query;
-}
-
/* Decode and analyze one instruction.
*
* Decodes the instructruction at @decoder->ip into @insn and @iext and updates
@@ -362,19 +329,20 @@
static int pt_ip_is_ahead(struct pt_insn_decoder *decoder, uint64_t ip,
size_t steps)
{
+ struct pt_insn_ext iext;
+ struct pt_insn insn;
struct pt_ild ild;
- uint8_t raw[pt_max_insn_size];
if (!decoder)
return 0;
/* We do not expect execution mode changes. */
ild.mode = decoder->mode;
- ild.itext = raw;
+ ild.itext = insn.raw;
ild.runtime_address = decoder->ip;
while (ild.runtime_address != ip) {
- int size, errcode, isid;
+ int size, errcode, relevant;
if (!steps--)
return 0;
@@ -382,8 +350,9 @@
/* If we can't read the memory for the instruction, we can't
* reach it.
*/
- size = pt_image_read(decoder->image, &isid, raw, sizeof(raw),
- &decoder->asid, ild.runtime_address);
+ size = pt_image_read(decoder->image, &insn.isid, insn.raw,
+ sizeof(insn.raw), &decoder->asid,
+ ild.runtime_address);
if (size < 0)
return 0;
@@ -393,11 +362,27 @@
if (errcode < 0)
return 0;
- errcode = pt_instruction_decode(&ild);
- if (errcode < 0)
- return 0;
+ relevant = pt_instruction_decode(&ild);
+ if (!relevant)
+ insn.iclass = ptic_other;
+ else {
+ if (relevant < 0)
+ return relevant;
- errcode = pt_insn_next_ip(&ild.runtime_address, &ild);
+ insn.iclass = pt_insn_classify(&ild);
+ }
+
+ insn.ip = ild.runtime_address;
+ insn.size = ild.length;
+
+ memset(&iext, 0, sizeof(iext));
+ iext.iclass = ild.iclass;
+ if (ild.u.s.branch_direct) {
+ iext.variant.branch.is_direct = 1;
+ iext.variant.branch.target = ild.direct_target;
+ }
+
+ errcode = pt_insn_next_ip(&ild.runtime_address, &insn, &iext);
if (errcode < 0)
return 0;
}
@@ -517,7 +502,7 @@
static int process_sync_disabled_event(struct pt_insn_decoder *decoder,
struct pt_insn *insn,
- const struct pt_ild *ild)
+ const struct pt_insn_ext *iext)
{
int errcode, iperr;
@@ -525,16 +510,31 @@
if (errcode <= 0)
return errcode;
- iperr = pt_insn_next_ip(&decoder->last_disable_ip, ild);
+ iperr = pt_insn_next_ip(&decoder->last_disable_ip, insn, iext);
if (iperr < 0) {
+ /* We don't know the IP on error. */
+ decoder->last_disable_ip = 0ull;
+
/* For indirect calls, assume that we return to the next
* instruction.
*/
- if (iperr == -pte_bad_query && ild->u.s.call)
- decoder->last_disable_ip =
- ild->runtime_address + ild->length;
- else
- decoder->last_disable_ip = 0ull;
+ if (iperr == -pte_bad_query) {
+ switch (insn->iclass) {
+ case ptic_call:
+ case ptic_far_call:
+ /* We only check the instruction class, not the
+ * is_direct property, since direct calls would
+ * have been handled by pt_insn_nex_ip() or
+ * would have provoked a different error.
+ */
+ decoder->last_disable_ip =
+ insn->ip + insn->size;
+ break;
+
+ default:
+ break;
+ }
+ }
}
return errcode;
@@ -945,14 +945,14 @@
pt_insn_changes_cpl(insn, iext) ||
pt_insn_changes_cr3(insn, iext))
return process_sync_disabled_event(decoder,
- insn, ild);
+ insn, iext);
} else if (ild->u.s.branch) {
if (!ild->u.s.branch_direct ||
ild->u.s.cond ||
ild->direct_target == ev->variant.disabled.ip)
return process_sync_disabled_event(decoder,
- insn, ild);
+ insn, iext);
}
return 0;