> The problem is that the only way to read the program counter is through jsr or an interrupt and these both can’t do relative jumps.

No, that's not a problem in this case since you don't need to know the value of the program counter to relocate the routine. You only need to know the relative offsets of the patch points from the start of the routine, which are static. The relocator itself is position independent, though, since it never uses absolute or absolute indexed addressing. No jumps or subroutine calls, just zero page loads and vectored writes.

That said, if you want to know the program counter, you can use a tiny, position independent subroutine to save it:

savepc
; copy return address to $fe-$ff
pla
sta $fe
pla
sta $ff
pha
lda $fe
pha

; increment $fe-$ff by 1 so that it contains the
; address of the next instruction after the jsr
inc $fe
bne done
inc $ff
done
rts

...

jsr savepc
lda #0 ; $fe-$ff contains address of this instruction after savepc call

That has no use in this relocation technique, however.

> If you start with that information and you allow patching you can of course relocate the code on the fly, but I wouldn’t really call that position independent code generation by a compiler.

Neither would I, since it's assembled, but that was never the question, as far as I understood. Quoting the post you first responded to: "You could write 6502 code to be completely relocatable, but at a cost of space and a more complex assembler." I demonstrated that with an example of a position independent relocator that patches an otherwise position dependent routine so that it can be relocated to the start of any page.

And yes, if you've built the module that you need to relocate, you have the information required to patch it. You could even join the position independent relocator with the relocatable routine and have it patch itself out with a jump to the routine after relocation.

> It’s more like you’re implementing your own linker.

Maybe a small portion, but specifically, it's a position independent code relocator, exactly what was on topic.

EDIT: I think we may be confusing two different problems here. One is relocating a routine and producing position independent code. That is what I've demonstrated. The other is the problem of knowing the location at the routine at the call site.

In the simple case, the call site of the relocator typically knows what portion of memory needs to be position adjusted. That is what I have assumed so far.

In a more complex case, e.g. a position independent/relocated routine needs to call another position independent/relocated routine without knowing its location. In order to do that you can put the address of each subroutine in a table and use it as a trampoline. The routines calling into each other now only need to know the index of the routine they want to call in the table. The table itself can be located anywhere and be moved around freely.

call ; $aa-ab = table location, y = subroutine index
lda ($aa),y
sta jump+1
iny
lda ($aa),y
sta jump+2
dey
jump
jmp $ffff

...
ldy #11 ; index of PRINT routine
jsr call

This can also be made position independent via stack trickery or indirect jump. The relocator for each routine could be responsible for populating this table.