OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 2:33 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 9 posts ] 
Author Message
 Post subject: GCC dont push args even with -mpush-args
PostPosted: Wed May 24, 2017 9:02 am 
Offline
Member
Member
User avatar

Joined: Sat May 20, 2017 1:25 am
Posts: 51
Location: PCI bus: 3, slot: 9, function: 5
The gcc dont push (assembly push) args even with:
-mpush-args -mno-accumulate-outgoing-args -mno-stack-arg-probe
But also there is no any error.

I tried it with 2 different versions of gcc.
What I need to do?

The reson I need this is this simple kernel:
Code:
extern char read_char(void); // read keyboard

void print(char letter,char row,char col){
   char*VGA=(char*) 0xB8000;
   VGA[(row*80+col)*2]=letter;
}

void main(void){
   print(read_char(),2,3);
}


But at assembly: (NASM)

Code:
extern read_char

main:
push   ebp
mov    ebp,esp

call read_char ;at eax the return

mov    eax,0x3 ;3rd arg the eax override! (have not the return of read_char anymore!)
mov    edx,0x2 ;2nd arg
mov    ecx,eax ;1st arg but here the gcc thinks the eax contains the return but the eax have been overridden!
call   print

pop ebp
ret


I Edited some things at print, In my code is correct but here I wrote it to fast ans I dont saw them!

_________________
How people react when a new update of your OS is coming:
Linux user: Cool, more free stuff!
Mac user: Ooh I have to pay!
Windows user: Ah not again!


Last edited by Coconut9 on Wed May 24, 2017 12:08 pm, edited 2 times in total.

Top
 Profile  
 
 Post subject: Re: GCC dont push args even with -mpush-args
PostPosted: Wed May 24, 2017 9:41 am 
Offline
Member
Member
User avatar

Joined: Sun Oct 22, 2006 7:01 am
Posts: 2646
Location: Devon, UK
Hi,

Several things to point out here. I'm not sure quite what you're tring to accomplish (well, I do in a 'meta' sense, but not programatically):


ARISTOS wrote:
Code:
   char*VGA=(char*) 0xB800;


Really?

Quote:
Code:
   VGA[row*80+col]=letter;


This index would be ok if you were using a pointer to the type uint16_t but how do you know the colour is not set as black on black?

Quote:
Code:
    print(read_char(),2,3);


This line would perhaps be OK if your functions work correctly although I'd object to the inconsistent function naming.

The assembly is where I really lose the plot. That doesn't look like a full like-for-like disassembly. Why the custom calling convention? Does read_char work as expected? What code does your environment produce without those switches? What does the actual disassembly look like?

Cheers,
Adam


Top
 Profile  
 
 Post subject: Re: GCC dont push args even with -mpush-args
PostPosted: Wed May 24, 2017 9:47 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4591
Location: Chichester, UK
This looks like the Microsoft fastcall convention, so I'm guessing that you are using CygWin, or something similar, on Windows.

The advice is always to use a cross-compiler, not a standard platform compiler, for OS work. I believe this would solve your problem.


Top
 Profile  
 
 Post subject: Re: GCC dont push args even with -mpush-args
PostPosted: Wed May 24, 2017 10:44 am 
Offline
Member
Member
User avatar

Joined: Fri Oct 27, 2006 9:42 am
Posts: 1925
Location: Athens, GA, USA
EDIT: Fnark, I just re-read the original post, and I think I was severely confused about the question. I'll leave my reply here for now while I go and do some research, as the disassembly you posted is crazy. As given it is not fastcall, or at least not correctly so; if it were, it would be:

Code:
mov    ecx, eax  ; 1st arg
mov    ecx, 0x2  ; 2nd arg
push   0x3       ; 3rd arg


I'll start with Wikipedia on x86 calling conventions.

The question about the non-standard calling convention still stands, though. Just what convention are you using, and in what order would things go if it were being used? Have you read the page on Calling Conventions, and if so, why use a non-standard one (assuming that is actually what you are doing, it isn't clear)? You might have a legitimate reason, but we'd need to know it if we are to help.

Also, according to the GCC docs, -mpush-args is already the default. It doesn't do what you seem to think it does, however. The purpose of it isn't to force the use of stack arguments (which is already the convention for both Linux and for Windows stdcall and cdecl - assume you want something similar to cdecl here), but to use the PUSH instruction instead of adding an offset to ESP and then using MOV to populate the arguments (which is an approach used on some RISC systems that don't have an explicit PUSH for when the registers allocated for args get overrun, but doesn't run as fast on x86 because of the optimizations on the PUSH, POP, CALL, and RET instructions). It doesn't change the calling convention, it just changes the specific instructions used to implement it - fastcall will still pass the first four arguments through registers (as will pretty much all the x86-64 calling conventions).

Similarly, -maccumulate-outgoing-args doesn't affect the calling convention; it tells the compiler to generate code in the function that pre-computes the maximum size of the stack frame, to make finding the end of a vararg stack faster.

Finally, -mno-stack-arg-probe is an undocumented option frankly shouldn't be used, period. Relying on it being there is simply foolish. In any case, the post you found on Sewage Overflow and the many references to it on this very forum are wrong, because it has nothing to do with changing the calling convention. As far as I can tell (which isn't very, since it is, you know, undocumented), it only affects certain debugger scaffolding which are used in testing GCC itself, and is the default anyway (since the compiler only generates that code when a different set of options are used to put it into a test state).

In other words, even if you have a reason to use a different calling convention, none of those options will do that. The previous posts basically lied recommending them - or at least were misleading on this topic, as they all seem to be copied from some earlier, inaccurate source.

In any case, every GCC p-mode calling convention used EAX and EDX for returns. That's not something you can do anything about.

So how do you change the calling convention? You don't, at least not to a custom convention. That would require you to add the support for it to GCC.

For an existing convention, you would need to use the appropriate function attribute in the code itself. For stdcall specifically, there is also -mrtd, but that won't help if what you want is cdecl.

However, the best solution is to use a cross-compiler configured to use cdecl and just not have to deal with that.

Finally,
  • Don't ever, ever, ever use void as the return for main() - it should always be int, period. Also, don't call the opening function of your OS main() in the first place, as the compiler generates a special header for main() functions, which wouldn't make sense here even if you have defined the appropriate header code for your OS (which you haven't, since you haven't set up a cross-compiler).
  • As has already been mentioned, you want 0xb8000, not 0xb800.
  • The text cells are not chars, and casting the page pointer to char is a Very Bad Idea. Define a two-byte struct type for the text cells and a set of functions for operating on it, and you'll avoid a lot of headaches later.

    Code:
    typedef struct TEXT_CELL
    {
        char glyph;
        uint8_t attrib;
    } text_cell;

  • Don't assume that you will always want the default page in any case. Instead, I recommend that you define a constant two-dimensional array for the page pointers, and use an index into that to select the page you want. Again, wrap all this up in suitable functions to hide the grotty details.

    Code:
    text_cell* pages[][] = { {( text_cell *) 0xb8000, ...}, {...},  ...};

    uint16_t mode_width[] = {80, 80, ...};   // horizontal size of each mode

    // I just used a macro for simplicity here,
    // I recommend using an inline function instead
    #define GET_XY(x, y, mode) ((y * mode_width[mode]) + x) 

    enum TEXT_MODES {DEFAULT, ...};

    uint8_t text_mode =  DEFAULT, page_selector = 0;
    uint16_t cell_x = 0, cell_y = 0;

    pages[text_mode][page_selector][GET_XY(cell_x, cell_y, text_mode)] = ...;

_________________
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.


Last edited by Schol-R-LEA on Wed May 24, 2017 11:55 pm, edited 5 times in total.

Top
 Profile  
 
 Post subject: Re: GCC dont push args even with -mpush-args
PostPosted: Wed May 24, 2017 12:14 pm 
Offline
Member
Member
User avatar

Joined: Sat May 20, 2017 1:25 am
Posts: 51
Location: PCI bus: 3, slot: 9, function: 5
Schol-R-LEA wrote:
EDIT: Fnark, I just re-read the original post, and I think I was severely confused about the question. I'll leave my reply here for now while I go and do some research, as the disassembly you posted is crazy. I'll start with Wikipedia on x86 calling conventions.

The question about the non-standard calling convention still stands, though. Just what convention are you using, and in what order would things go if it were being used? Have you read the page on Calling Conventions, and if so, why use a non-standard one (assuming that is actually what you are doing, it isn't clear)? You might have a legitimate reason, but we'd need to know it if we are to help.

Also, according to the GCC docs, -mpush-args is already the default. It doesn't do what you seem to think it does, however. The purpose of it isn't to force the use of stack arguments (which is already the convention for both Linux and for Windows stdcall and cdecl - assume you want something similar to cdecl here), but to use the PUSH instruction instead of adding an offset to ESP and then using MOV to populate the arguments (which is an approach used on some RISC systems that don't have an explicit PUSH for when the registers allocated for args get overrun, but doesn't run as fast on x86 because of the optimizations on the PUSH, POP, CALL, and RET instructions). It doesn't change the calling convention, it just changes the specific instructions used to implement it - fastcall will still pass the first four arguments through registers (as will pretty much all the x86-64 calling conventions).

Similarly, -maccumulate-outgoing-args doesn't affect the calling convention; it tells the compiler to generate code in the function that pre-computes the maximum size of the stack frame, to make finding the end of a vararg stack faster.

Finally, -mno-stack-arg-probe is an undocumented option frankly shouldn't be used, period. Relying on it being there is simply foolish. In any case, the post you found on Sewage Overflow and the many references to it on this very forum are wrong, because it has nothing to do with changing the calling convention. As far as I can tell (which isn't very, since it is, you know, undocumented), it only affects certain debugger scaffolding which are used in testing GCC itself, and is the default anyway (since the compiler only generates that code when a different set of options are used to put it into a test state).

In other words, even if you have a reason to use a different calling convention, none of those options will do that. The previous posts basically lied recommending them - or at least were misleading on this topic, as they all seem to be copied from some earlier, inaccurate source.

In any case, every GCC p-mode calling convention used EAX and EDX for returns. That's not something you can do anything about.

So how do you change the calling convention? You don't, at least not to a custom convention. That would require you to add the support for it to GCC.

For an existing convention, you would need to use the appropriate function attribute in the code itself. For stdcall specifically, there is also -mrtd, but that won't help if what you want is cdecl.

However, the best solution is to use a cross-compiler configured to use cdecl and just not have to deal with that.

Finally,
  • Don't ever, ever, ever use void as the return for main() - it should always be int, period. Also, don't call the opening function of your OS main() in the first place, as the compiler generates a special header for main() functions, which wouldn't make sense here even if you have defined the appropriate header code for your OS (which you haven't, since you haven't set up a cross-compiler).
  • As has already been mentioned, you want 0xb8000, not 0xb800.
  • The text cells are not chars, and casting the page pointer to char is a Very Bad Idea. Define a two-byte struct type for the text cells and a set of functions for operating on it, and you'll avoid a lot of headaches later.

    Code:
    typedef struct TEXT_CELL
    {
        char glyph;
        uint8_t attrib;
    } text_cell;

  • Don't assume that you will always want the default page in any case. Instead, I recommend that you define a constant two-dimensional array for the page pointers, and use an index into that to select the page you want. Again, wrap all this up in suitable functions to hide the grotty details.

    Code:
    text_cell* pages[][] = { {( text_cell *) 0xb8000, ...}, {...},  ...};

    uint16_t mode_width[] = {80, 80, ...};   // horizontal size of each mode

    // I just used a macro for simplicity here,
    // I recommend using an inline function instead
    #define GET_XY(x, y, mode) ((y * mode_width[mode]) + x) 

    enum TEXT_MODES {DEFAULT, ...};

    uint8_t text_mode =  DEFAULT, page_selector = 0;
    uint16_t cell_x = 0, cell_y = 0;

    pages[text_mode][page_selector][GET_XY(cell_x, cell_y, text_mode)] = ...;


In my code all was correct but here I wrote it fast (I didn't copy them) so I wasn't saw the problems.
In my code I use char because I have one more argument witch sets the color.
Why not void main (I know it for windows but why)?

_________________
How people react when a new update of your OS is coming:
Linux user: Cool, more free stuff!
Mac user: Ooh I have to pay!
Windows user: Ah not again!


Top
 Profile  
 
 Post subject: Re: GCC dont push args even with -mpush-args
PostPosted: Wed May 24, 2017 12:16 pm 
Offline
Member
Member
User avatar

Joined: Sat May 20, 2017 1:25 am
Posts: 51
Location: PCI bus: 3, slot: 9, function: 5
iansjack wrote:
This looks like the Microsoft fastcall convention, so I'm guessing that you are using CygWin, or something similar, on Windows.

The advice is always to use a cross-compiler, not a standard platform compiler, for OS work. I believe this would solve your problem.


Can you suggest me any compiler?

_________________
How people react when a new update of your OS is coming:
Linux user: Cool, more free stuff!
Mac user: Ooh I have to pay!
Windows user: Ah not again!


Top
 Profile  
 
 Post subject: Re: GCC dont push args even with -mpush-args
PostPosted: Wed May 24, 2017 12:21 pm 
Offline
Member
Member

Joined: Wed Jul 10, 2013 9:11 am
Posts: 51
ARISTOS wrote:
In my code all was correct but here I wrote it fast (I didn't copy them) so I wasn't saw the problems.
In my code I use char because I have one more argument witch sets the color.
Why not void main (I know it for windows but why)?


`void main` has never been legal C or C++ code. However, it compiles in ancient DOS compilers like Turbo C which is still in use today. Even if it works on whatever compiler you're using, you shouldn't name your entry point `main` but rather something like `kmain`.

Also `0xB800` is the wrong address. It should be `0xB8000`. See https://en.wikipedia.org/wiki/Hexadecimal.


Top
 Profile  
 
 Post subject: Re: GCC dont push args even with -mpush-args
PostPosted: Wed May 24, 2017 12:37 pm 
Offline
Member
Member
User avatar

Joined: Fri Oct 27, 2006 9:42 am
Posts: 1925
Location: Athens, GA, USA
First, as for the cross-compiler, that's not a matter of using a different compiler, but of using a separate build of the GCC compiler. The wiki covers the process for creating a GCC Cross Compiler in detail (follow the given link).

Regarding void main(): the standard for C has always been that int main() is the correct, portable form. This was necessary because Unix, which was C's native environment, required every program to have an integer return value for passing error messages to the shell. However, in the original K&R form, an int return with a value of 0 (which in Unix indicates 'program completed with no errors') was the default for all functions, so most programs wrote it as:

Code:
main() {
/* ... */
}


Furthermore, the compilers of the time did little type checking, and would allow a function to return with or without a value regardless of how it was declared.

When the language was formally standardized in 1989, it was sensibly argued that a default int was a problem for anything that didn't have a return value, so void was added for non-returning functions, and returns were required to be type checked. Only a void function could now end without an explicit return statement.

However, when they were defining the behavior of main(), it was pointed out that some OSes didn't support a program return the way Unix did, and that it would present an issue with embedded code as well. Thus, it was ruled that a compiler could optionally allow a void main(), but that this would be non-standard - it was intended that only compilers for systems where a program return was a problem would use it, and int main() would be used everywhere it could be. A compliant C compiler would still need to use int main().

Unfortunately, this was worded in a tremendously confusing fashion. As a result, a lot of MS-DOS compiler developers decided that this stipulation applied to them, even though MS-DOS did support program returns after version 2.x (which borrowed a lot of things from Unix such as pipes and directories), and they didn't get that this moved them out of compliance. Thus, there was a proliferation of programs that used void main(), and there was massive confusion about the subject - I recall being told that void was the new standard when I was learning C, and didn't get the full story until much later.

Confusing this further was the fact that in C++, void main() was an error from the start, but several non-compliant compilers allowed it.

The early 16-bit Turbo C and C++ compilers were particularly problematic about this, because they were based on a draft of the C standard, not the actual released version, and so had a huge pile of incompatibilities with the actual C89 standard, as well as dumping several non-standard libraries into the mix without mentioning in the manuals that they weren't portable (<console.h> is the worst offender in this).

(They are also problematic for C++ specifically because C++ wasn't standardized yet, so they were free to interpret Stroustrup's book any way they liked. However, the way that the standard library headers are named was changed when that language was standardized in 1998, meaning that any C++ code written for older compilers needed to be changed when porting to something newer, and confusing the heck of students who learned on older versions. But that's a separate issue.)

Worse still, several countries (most notably India and Pakistan) standardized on those compilers very early on, and still use them to this day despite their age (release dates 1988 and 1990, respectively) and reliance on MS-DOS support. Students are still taught to use those problematic features, even though no modern compiler supports them.

Eventually, the C99 standard revision deprecated void main(), and later still it was removed in C11. However, by this time the damage had been done.

The change in the standard also means that programs which have no return value, such as embedded systems or operating systems, need to use a non-standard entry point, rather than main(). Since main() usually also does a number of other things in a hidden program prefix/header on most systems, using main() for such things was a poor choice anyway, so everyone agreed that requiring int main() wouldn't be a problem after all. Thus, the recommendation to use something such as void _entry() or void _kmain() instead.

Anyway, the lesson is that it was always wrong, but for a time a lot of people didn't know that.

_________________
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.


Top
 Profile  
 
 Post subject: Re: GCC dont push args even with -mpush-args
PostPosted: Wed May 24, 2017 7:51 pm 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 671
Should point out that even by the latest C11 standards a compiler is free to implement `void main` if it so chooses in the case of a non-hosted environment, as long as it is documented that way. By using using -ffreestanding in GCC you are no longer bound by `int main` as the signature of main is now implementation defined. As far as I know GCC is documented in such a way that in freestanding mode the entry point (including but not limited to main) can be any function with any signature the developer chooses.

GCC's documentation related to non-hosted freestanding environment is documented as follows:
Quote:
To make it act as a conforming freestanding implementation for a freestanding environment, use the option -ffreestanding; it then defines __STDC_HOSTED__ to 0 and does not make assumptions about the meanings of function names from the standard library, with exceptions noted below. To build an OS kernel, you may well still need to make your own arrangements for linking and startup. See Options Controlling C Dialect.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 76 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group