Question

I am writing a bit of 16-bit (pun intended) code in C++, compiling it with G++. More on the context I'm compiling in here: Force GCC to push arguments on the stack before calling function (using PUSH instruction)

The problem I am facing now is regarding an error LD throws when trying to link my object files. Specifically, here's a code situation:

asm(".code16gcc\n");
void f(const char*);
int main(){
    f("A constant string put in section .rodata at link-time");
}
void f(const char* s){ }

In assembly code, with -S and -mno-accumulate-outgoing-args options G++ would translate this to (only relevant parts of the assembly written):

/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("A constant string put in section .rodata at link-time");
    push    OFFSET FLAT:.LC0
    call    _Z1fPKc

This application is part of an OS I'm developing. Specifically, the bootloader loads this code at address 0x70D00 in BIOS memory. That makes .rodata's address be bigger than 0x70D00. Since GCC does not have built-in support for pure 16-bit code, it doesn't know that executing the 'push OFFSET FLAT:.LC0' would mean pushing a WORD UNDER PURE 16-BIT circumstances. Which means that, if the address of .rodata is - say - 0x70DAA, the instruction would be 'push 0x70DAA'. That's why the linker throws the error:

In function main': relocation truncated to fit: R_386_16 against.rodata'

-- because the linker knows that 0x70DAA DOES NOT FIT IN A WORD. What would solve the problem is asking GCC to MOV the arguments IN A REGISTER BEFORE PUSHING THEM. Something like:

/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("A constant string put in section .rodata at link-time"); , now using EAX before pushing the string literal's offset in .rodata
    mov eax, OFFSET FLAT:.LC0 // move in eax instead
    push    eax // and push eax!
    call    _Z1fPKc

This is what MSVC does to optimize in some situations. I was wondering if there's a way to force GCC to do the same thing...one alternative that apparently would work is associating the attribute((regparm(N))) to function f. But this is not really a good alternative, since it DOESN'T REALLY PUSH the registers on the stack, rather than using them directly in f - and can't do this for any function. You can find out more on this by doing a short google search and if needed I'll post exactly what this option does here and why it would't really work, but this question-post starts to get too long.

In short, my question is: Can I ask GCC to MOV the arguments passed to functions IN A REGISTER BEFORE PUSHING THEM?

Thanks in advance!

Était-ce utile?

La solution

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top