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;