3

I've been working through Programming from the Ground Up book on an old Dell Dimension PC, bought circa 2006, with 32-bit architecture and running a 32-bit Xubuntu installation. I enjoyed the first 6 chapters, and feel like I am 'getting' assembly language so far and giving myself a decent foundation for further study.

However, I'm stuck on the Chapter 7 example (page 125 as regards what's displayed in the book; page 131 as the PDF reader considers it to be). Specifically, when I add my own non-conditional jump immediately after the open call to allow me to move the value of %eax to %ebx and therefore inspect it with echo $?:

  • When the expected input file does exist, the call to Linux call #5 (open) returns 3 consistently. This seems to make sense: a successful call, should, based on a successful open, return a non-zero file descriptor.
  • The problem: when the input file does not exist, the Linux call #5 returns 254. Based on what I've read in the book I am working on, plus some man page checks and Googling, I would have expected a negative value. This should have caused the program, as written in the book, to throw an error due to a negative value in %eax; but that doesn't happen.

In setting up the 'does not exist' scenario, I have tried the following tweaks as regards the input file:

  1. Renamed it with a '.mv suffix'.
  2. Moved it to the Waste Basket.
  3. Adjusted the source file add-year.s to expect a totally different input file with a long, almost-certainly-unique name which couldn't be expected to exist elsewhere on my file system.

In all 3 cases, system call #5 returns 254. I wondered, perhaps if it doesn't find the file, it creates it? But the book in front of me states that moving the literal $0 into register %ecx instructs system call #5 to open a file for read-only.

From my Googling, it seems that assembly language directly hitting system calls is not the normal way to do things; in practice, programmers would call Linux C wrapper programs from C. This makes sense - and I'm keen to learn C in the future - however, it would be great if anyone who's worked through this book (or is just a wizard anyway) can help with this specific error, to help myself (and hopefully other readers of this post) work through this book's examples without having to admit defeat.

jww
  • 97,681
  • 90
  • 411
  • 885
  • 2
    The system call actually does not return 254, it returns -2. It's just that the exit system call that you use to extract it gives you the low 8 bits as an unsigned value. See [man 3 exit](http://man7.org/linux/man-pages/man3/exit.3.html) in particular: _"the value of status & 0377 is returned to the parent"_ – Jester Nov 12 '19 at 00:59
  • 1
    Use `strace ./my_program` to decode system call args and return values. It's so good it can basically replace error handling in toy programs that you're writing to experiment with system calls. Or at least use a debugger to look at EAX and EBX before the exit system call. And BTW `-ENOENT` is in fact `-2`, which is `254` as a `uint8_t`. – Peter Cordes Nov 12 '19 at 01:05
  • 1
    And BTW, you don't have to suffer with an old machine just to use 32-bit code. You can use a modern 64-bit Linux distro, just build with `gcc -m32 -no-pie add-year.s` (and whatever other options you need, e.g. `-nostdlib` if you write your own `_start` instead of a `main`.) You may need to install a "multilib" gcc package for 32-bit libc, but you can build 32-bit static executables with just native gcc + binutils. – Peter Cordes Nov 12 '19 at 01:10
  • 1
    And BTW, if `ls -l filename` says the file doesn't exist, and `open()` system call will get the same result. (And you can check again after to make sure you didn't pass `O_CREAT` and have `open` create the file for you.) – Peter Cordes Nov 12 '19 at 01:18
  • Thanks a lot for the good answers here. Gets me progressing again but with some added tools to try out. – GeordieProgrammer Nov 12 '19 at 23:46
  • 1/2 If anyone specifically studying 'Programming From The Ground Up' might be interested, here's some output from following through the responses to this question. Here, I'm running Mint 19.2 Cinnamon 64-bit on a Medion Akoya model MS7848 desktop PC I bought in 2015. Notice how the compile/link of the 32-bit assembly language on a 64-bit machine 'just works'. Also notice the great feedback from strace, which would have prevented me needing to ask this question (although worse things could have happened to the world, given the high quality of all the answers I got): – GeordieProgrammer Nov 13 '19 at 02:13
  • 2/2 MS7848 64-bit Mint 19.2:~$ gcc -m32 -no-pie -nostdlib -o add-year add-year.s write-newline.s error-exit.s read-record.s write-record.s count-chars.s MS7848 64-bit Mint 19.2:~$ strace ./add-year execve("./add-year", ["./add-year"], 0x7ffd3cf48480 /* 51 vars */) = 0 strace: [ Process PID=3543 runs in 32 bit mode. ] open("test.dat", O_RDONLY) = -1 ENOENT (No such file or directory) exit(-2) = ? +++ exited with 254 +++ MS7848 64-bit Mint 19.2:~$ – GeordieProgrammer Nov 13 '19 at 02:14
  • To wrap this up, again for the benefit of anyone else reading the specified book: I have followed up on the unexpected behaviour of the book's example program (this is what first set me down the path of inspecting system call return values, rather than just black box testing 2 different possible execution paths. Of course, with the benefit of strace, such testing is no longer 'black box'.). On page 125, 'jl continue_processing' should be 'jg continue_processing'. – GeordieProgrammer Nov 13 '19 at 23:48

0 Answers0