With awk
awk -F '[ \t]+$' 'NF>1{t=substr($0,length($1)+1);gsub(/./,"_",t); $0=$1 t} 1'
This also handles a mixture of trailing tabs and spaces. The field separator (-F, FS) can be easily adjusted to only match spaces or also match other kind of invisible characters, provided that it's kept anchored at the end with $.
To make that work for leading blanks, everything should be mirrored not just $ to ^:
awk -F '^[ \t]+' 'NF>1{h=substr($0,1,length()-length($2));gsub(/./,"_",h); $0=h $2} 1'
To make it work for both leading and trailing blanks, the logic should be inverted; set the field separator to a pattern not matching leading and trailing blanks:
awk -F '[^ \t](.*[^ \t]|$)' '{s=$0; h=gsub(/./,"_",$1); t=gsub(/./,"_",$2); print $1 substr(s,h+1, length(s)-h-t) $2}'
Or the same with adjustable pattern:
awk -v ns='[^ \t]' 'BEGIN{FS=ns"(.*"ns"|$)"}{s=$0; h=gsub(/./,"_",$1); t=gsub(/./,"_",$2); print $1 substr(s,h+1, length(s)-h-t) $2}'
Different from @EdMorton's solutions, these handle correctly lines which contain only spaces and will work with any implementation of awk, not just GNU awk (gawk): eg. with mawk or bwk ("original-awk"), which are both much faster than gawk. But even when used with gawk, the last solution will be almost twice as fast as @EdMorton's.
With sed
With sed, the only solution I can think of is to substitute repeatedly in a loop; if there are many trailing spaces and long lines, this can get slow fast:
sed -e :x -e 's/ \( *\)$/_\1/;tx'
Notice that sed ':x;s/ \( *\)$/_\1/;tx' is not standard sed; :label is not one of the commands which can be terminated by a ;:
Editing commands other than {...}, a, b, c, i, r, t, w, :, and # can be followed by a <semicolon>, optional <blank> characters, and another editing command.
With perl
Here is an alternate perl solution, which is NOT really an improvement upon the existing perl answer, but which, since it doesn't use the e flag of s///, could be theoretically adapted to some other tool providing a sed-like s/// and perl/pcre-like zero-width assertions in its regexes:
perl -ple 's/\s(?=\s*$)/_/g'