diff --git a/core/arch/arm/arm.mk b/core/arch/arm/arm.mk
index be53df2..f03bf6c 100644
--- a/core/arch/arm/arm.mk
+++ b/core/arch/arm/arm.mk
@@ -89,7 +89,9 @@ platform-aflags-generic ?= -pipe
 
 arm32-platform-cflags-no-hard-float ?= -mfloat-abi=soft
 arm32-platform-cflags-hard-float ?= -mfloat-abi=hard -funsafe-math-optimizations
-arm32-platform-cflags-generic ?= -mthumb -mthumb-interwork \
+arm32-platform-cflags-generic-thumb ?= -mthumb -mthumb-interwork \
+			-fno-short-enums -fno-common -mno-unaligned-access
+arm32-platform-cflags-generic-arm ?= -marm \
 			-fno-short-enums -fno-common -mno-unaligned-access
 arm32-platform-aflags-no-hard-float ?=
 
@@ -138,7 +140,7 @@ core-platform-cflags += $(arm32-platform-cflags-no-hard-float)
 ifeq ($(CFG_UNWIND),y)
 core-platform-cflags += -funwind-tables
 endif
-core-platform-cflags += $(arm32-platform-cflags-generic)
+core-platform-cflags += $(arm32-platform-cflags-generic-thumb)
 core-platform-aflags += $(core_arm32-platform-aflags)
 core-platform-aflags += $(arm32-platform-aflags)
 endif
@@ -166,7 +168,17 @@ ta_arm32-platform-cflags += $(arm32-platform-cflags)
 ta_arm32-platform-cflags += $(platform-cflags-optimization)
 ta_arm32-platform-cflags += $(platform-cflags-debug-info)
 ta_arm32-platform-cflags += -fpic
-ta_arm32-platform-cflags += $(arm32-platform-cflags-generic)
+
+# Thumb mode doesn't support function graph tracing due to missing
+# frame pointer support required to trace function call chain. So
+# rather compile in ARM mode if function tracing is enabled.
+ifeq ($(CFG_TA_FTRACE_SUPPORT),y)
+ta_arm32-platform-cflags += $(arm32-platform-cflags-generic-arm) \
+			-fno-omit-frame-pointer -mapcs
+else
+ta_arm32-platform-cflags += $(arm32-platform-cflags-generic-thumb)
+endif
+
 ifeq ($(arm32-platform-hard-float-enabled),y)
 ta_arm32-platform-cflags += $(arm32-platform-cflags-hard-float)
 else
diff --git a/core/arch/arm/kernel/ftrace.c b/core/arch/arm/kernel/ftrace.c
index 4b4a49b..8f34c6a 100644
--- a/core/arch/arm/kernel/ftrace.c
+++ b/core/arch/arm/kernel/ftrace.c
@@ -23,11 +23,12 @@ void ta_fbuf_init(struct ta_head *ta_head, struct tee_ta_session *s)
 	size_t fbuf_size = 0;
 	int count = 0;
 
-	if (ta_head->ftrace_buf_end > ta_head->ftrace_buf_start)
-		fbuf_size = ta_head->ftrace_buf_end - ta_head->ftrace_buf_start;
+	if (ta_head->ftrace_buf_end.ptr64 > ta_head->ftrace_buf_start.ptr64)
+		fbuf_size = ta_head->ftrace_buf_end.ptr64 -
+				 ta_head->ftrace_buf_start.ptr64;
 
 	if (fbuf_size >= MIN_FTRACE_BUF_SIZE) {
-		fbuf = (struct ftrace_buf *)ta_head->ftrace_buf_start;
+		fbuf = (struct ftrace_buf *)ta_head->ftrace_buf_start.ptr64;
 		fbuf->head_off = sizeof(struct ftrace_buf);
 		count = snprintk((char *)fbuf + fbuf->head_off,
 				 MAX_HEADER_STRLEN,
diff --git a/lib/libutee/arch/arm/utee_mcount_a32.S b/lib/libutee/arch/arm/utee_mcount_a32.S
index 0626baa..8923b8a 100644
--- a/lib/libutee/arch/arm/utee_mcount_a32.S
+++ b/lib/libutee/arch/arm/utee_mcount_a32.S
@@ -26,25 +26,36 @@
  */
 FUNC __gnu_mcount_nc, :
 	stmdb		sp!, {r0-r3, lr}
-	ldr		r0, [sp, #20]		/* lr of instrumented func */
 #ifdef CFG_TA_GPROF_SUPPORT
+	ldr		r0, [sp, #20]		/* lr of instrumented func */
 	mcount_adj_pc	r0, r0
 	mcount_adj_pc	r1, lr			/* instrumented func */
 	bl		__mcount_internal
 #endif
+#ifdef CFG_TA_FTRACE_SUPPORT
+	/* Get instrumented function's pc value */
+	ldr		r0, [sp, #16]
+	mcount_adj_pc	r0, r0
+	/* Get instrumented function's lr address pointer */
+	sub		r1, fp, #4
+	bl		ftrace_enter
+#endif
 	ldmia		sp!, {r0-r3, ip, lr}
 	bx		ip
 END_FUNC __gnu_mcount_nc
 
 #ifdef CFG_TA_FTRACE_SUPPORT
-/*
- * ftrace is not yet supported in 32bit mode. Currently this is just a
- * placeholder to avoid linking error.
- */
 FUNC __ftrace_return, :
-	push		{lr}
-	pop		{ip, lr}
-	bx		ip
+	/* save return value regs */
+	stmdb		sp!, {r0-r3}
+
+	/* get return address of parent func */
+	bl		ftrace_return
+	mov		lr, r0
+
+	/* restore return value regs */
+	ldmia		sp!, {r0-r3}
+	bx		lr
 END_FUNC __ftrace_return
 #endif
 
diff --git a/lib/libutee/include/user_ta_header.h b/lib/libutee/include/user_ta_header.h
index e2445b2..63e5d82 100644
--- a/lib/libutee/include/user_ta_header.h
+++ b/lib/libutee/include/user_ta_header.h
@@ -27,9 +27,9 @@
 
 #define TA_FLAGS_MASK			GENMASK_32(9, 0)
 
-union ta_head_func_ptr {
+union compat_ptr {
 	uint64_t ptr64;
-	struct ta_head_func_ptr32 {
+	struct compat_ptr32 {
 		uint32_t lo;
 		uint32_t hi;
 	} ptr32;
@@ -39,10 +39,10 @@ struct ta_head {
 	TEE_UUID uuid;
 	uint32_t stack_size;
 	uint32_t flags;
-	union ta_head_func_ptr entry;
+	union compat_ptr entry;
 #ifdef CFG_TA_FTRACE_SUPPORT
-	uintptr_t ftrace_buf_start;
-	uintptr_t ftrace_buf_end;
+	union compat_ptr ftrace_buf_start;
+	union compat_ptr ftrace_buf_end;
 #endif
 };
 
diff --git a/ta/arch/arm/user_ta_header.c b/ta/arch/arm/user_ta_header.c
index 5496294..d294f1f 100644
--- a/ta/arch/arm/user_ta_header.c
+++ b/ta/arch/arm/user_ta_header.c
@@ -60,8 +60,13 @@ const struct ta_head ta_head __section(".ta_head") = {
 	.entry.ptr64 = (uint64_t)__utee_entry,
 #endif
 #ifdef CFG_TA_FTRACE_SUPPORT
-	.ftrace_buf_start = (uintptr_t)&__ftrace_buf_start,
-	.ftrace_buf_end = (uintptr_t)__ftrace_buf_end,
+#ifdef __ILP32__
+	.ftrace_buf_start.ptr32 = { .lo = (uint32_t)&__ftrace_buf_start },
+	.ftrace_buf_end.ptr32 = { .lo = (uint32_t)__ftrace_buf_end },
+#else
+	.ftrace_buf_start.ptr64 = (uint64_t)&__ftrace_buf_start,
+	.ftrace_buf_end.ptr64 = (uint64_t)__ftrace_buf_end,
+#endif
 #endif
 };
 
