<!-- language-all: lang-el -->

The command `end-of-buffer` uses a variation on `(goto-char (point-max))` to go to the last position in the buffer. If the buffer ends with a newline, `end-of-buffer` will leave you on the empty line at the end, whereas if you delete this last newline, `end-of-buffer` will leave you at the end of the text in the now non-empty last line.

One possible solution is to advise `end-of-buffer` to just go up a line if it leaves you on an empty line:

    (defun my-end-of-buffer-dwim (&rest _)
      "If current line is empty, call `previous-line'."
      (when (looking-at-p "^$")
        (previous-line)))

    (advice-add #'end-of-buffer :after #'my-end-of-buffer-dwim)

If you want `end-of-buffer` to always leave you at the *beginning* of the penultimate line (rather than at the end if the file has no trailing newline), you might want this instead:

    (defun my-end-of-buffer-dwim (&rest _)
      "Go to beginning of line.
    If current line is empty, go to beginning of previous one
    instead."
      (beginning-of-line (and (looking-at-p "^$") 0)))

    (advice-add #'end-of-buffer :after #'my-end-of-buffer-dwim)

And the standard advice disclaimer: You can avoid unanticipated side-effects by just creating your own end-of-buffer command and rebinding the keys, rather than using advice:

    (defun my-end-of-buffer ()
      "Go to beginning of last line in buffer.
    If last line is empty, go to beginning of penultimate one
    instead."
      (interactive)
      (goto-char (point-max))
      (beginning-of-line (and (looking-at-p "^$") 0)))

    (global-set-key [remap end-of-buffer] #'my-end-of-buffer)

Note that all of the above solutions leave you at most one line away from the end of the buffer - they do not leave you on the *last* non-empty line.