Innanzitutto a me non dà errori di assegnamento con:

struttura2 = funx01();

l'unica cosa da tenere presente è che la struc ritornata dalla funzione è ritornata 'per valore', nel senso che i valori valori della struct ritornata venono ricopiati in 'struttura2'; devi infatti sapere che in c tutte le variabili allocate staticamente (ovvero senza usare la malloc), vengono automaticamente distrutte all'uscita di una funzione, non è come in java in cui puoi costruire un ogetto in una funzione e ritornarlo, e ritorni proprio l'oggetto costruito nella funzione. In c per fare questo allochi dinamicamente memoria per la struct, e poi ritorni il puntatore alla struct, che non sarà distrutta all'uscita della funzione, proprio perche allocata dinamicamente. In effetti questo è ciò che fai anche in java, perche in realtà in java gli oggetti altro non sono che puntatori agli ogetti veri e propri.

A dimostrazione di quanto detto, puoi usare il tuo programma, stampando l'indirizzo delle variabili usate nella funzione e nel main: puoi osservare che l'indirizzo della variabile struttura2 è sempre quello iniziale, anche dopo l'assegnamento:

codice:
#include <stdio.h>

struct a{
 int val1;
 int val2;
};

struct a funx01(){
  struct a struttura;
  struttura.val1 = 12;
  printf("indirizzo della variabile nella funzione: %d\n", &struttura);
  return struttura;
}

int main()
{
  struct a struttura2;
  printf("i ndirizzo della variabile nel main prima: %d\n", &struttura2);
  struttura2 = funx01();
  printf("indirizzo della variabile nel main dopo: %d\n", &struttura2);
  return 0;
}