I have thought of a work-around for this problem, although I would have prefered a MOV-to-REG-and-PUSH sort-of method. What I've thought of is that this only happens for addresses that the compiler can calculate at compile time, like the address of the string which was put in .rodata.
Knowing that, I have created a local variable in main and used that as the passed argument instead, like this:
asm(".code16gcc\n");
void f(const char*);
int main(){
const char* s = "A constant string put in section .rodata at link-time";
// Now use 's' as the argument instead of the string literal
f(s);
}
void f(const char* s){ }
This effectively changes the generated assembly code to:
/APP
.code16gcc
.section .rodata
.LC0:
.string "A constant string put in section .rodata at link-time"
main:
.LFB0:
/* here would be main's prologue, not put because it ain't relevant */
// THIS IS THE CALL f(s);
mov DWORD PTR [ebp-12], OFFSET FLAT:.LC0 // now specifically loaded in the DWORD 's'
sub esp, 12
push DWORD PTR [ebp-12]
call _Z1fPKc
As it can be seen, the local variable is used now instead, the address to the string literal (in .rodata) being transferred specifically in a DWORD. This effectively avoids the linker error, although it uses some neglijable extra stack space.