views:

105

answers:

4

According to the documentation for fgets(), the function takes three parameters:

  • char * - a string that will hold the input
  • int - an integer that represents the maximum number of characters to read
  • FILE * - a FILE * to the stream to read from

I have no trouble calling the function. I just push the three parameters onto the stack, call the function, and increase ESP by 12.

My problem is with parameter #3. What should be passed in as the FILE * for standard input? In C, I can just use stdin, but I don't know what the equivalent is in x86 assembly.


Update: I'm using NASM on Linux.

+1  A: 

If the assembly is a subroutine to C/C++ code, most runtime environments provide a means of directly accessing the stdin variable through an external reference. Check the stdio.h header file (or maybe whatever it includes). The usual suspects seem to be variables named __stdin or an array of FILE * named something like __stdio[] where the first 3 elements are stdin, stdout, and stderr.

If C is being used as a library for some other language (like assembly), then you'll have to call the C runtime init yourself. That can be tricky to identify. If I had no idea how, I'd write a "hello world" type C program and step through it with a debugger to see how it sets up stdin.

Another completely different approach would be to call fopen() to obtain a FILE * of a file to read.

wallyk
This is pure assembly making use of `libc`.
George Edison
A: 

stdin is a concept which only exists in C. Its definition depends on your C compiler and library, and due to macros and the like, it may be very difficult to invoke from assembler.

One thing you could try is treating it as if stdin was a global variable of register size. Load it into a register (using whatever name mangling conventions your C compiler uses), then push it onto the stack. If this doesn't work, you'll need to examine the C library source code to see how it does things.

Alternately, you could use lower-level operating system calls that may be more amenable to assembler usage - however, since you didn't specify your OS, it's hard to be more specific.

bdonlan
Sorry, I'm working with Linux using `libc`. My assembler is NASM.
George Edison
+4  A: 

If you're willing to sacrifice stdio and use POSIX calls instead, stdin has the well-known file descriptor ID 0. You can therefore pass 0 to read and get almost what you were looking for. I'm pretty sure this is more assembler-friendly than the stdin C macro.

zneak
This looks promising. So basically call `read()` passing 0 as the first parameter?
George Edison
@George Edison Exactly.
zneak
@zneak: +500 if I could! This worked just perfect. Thank you!
George Edison
Note that this won't have the same stop-at-end-of-line semantics as `fgets` - it might look like it, because it'll unblock when a line comes through in the default terminal mode, but in some cases it may send through more than one line at once, or in unbuffered terminal modes, partial lines.
bdonlan
Well, I had to add a couple instructions to truncate the string before the newline, since it includes that in the string.
George Edison
@George Edison Do note that @bdonlan is right: `read` is not a POSIX clone of `fgets`, and there might be some additional treatment required.
zneak
A: 

This is pure assembly making use of libc. – George Edison

Then the answer will depend entirely on the development system and operating system. Libc is not aimed at supporting this kind of thing.

Even if you could figure out how to do this kind of call, the stdin points to a fairly complex, OS- or development system-dependent FILE data structure which is initialized by libc using routines that are called before main() is run. So in pure assembly, you'd have to do all that, too. This kind of thing is why a simple C-language "Hello world" program makes a fairly good-sized executable on any platform.

If you write a simple C program that reads some info from stdin, and then disassemble the whole thing and understand exactly what it's doing, you'll have a good start on this. But it won't be quick to do this, and what you learn certainly won't be portable from, say, Visual Studio on Windows to gcc on Linux.

Bob Murphy
"So in pure assembly, you'd have to do all that, too." Not quite. Just linking with `libc` is enough to have the structures for `std` file pointers automagically constructed before you hit `main`. The point is mostly to find to what the macros expand to and how you can use that result, and this is indeed platform-dependant and complex.
zneak
Not quite. Don't forget that it's `libc` that's calling the `main()` function in my assembly code.
George Edison