A thing all these programs had in common was their use of the 09h function of INT 21h for printing the “hello, world!” string. But it’s time to move forward. Now I plan to use the lovely C printf function.
Finally, it’s time to switch to the fabulous GNU as. We’ll forget about DEBUG for some time. Thanks DEBUG. GNU as, Gas, or the GNU Assembler, is obviously the assembler used by the GNU Project. It is part of the Binutils package, and acts as the default back-end of gcc. Gas is very powerful and can target several computer architectures. Quite a program, then. As most assemblers, Gas’ input is comprised of directives (also referred to as Pseudo Ops), comments, and of course, instructions. Instructions are very dependent on the target computer architecture. Conversely, directives tend to be relatively homogeneous.
Originally, this assembler only accepted the AT&T assembler syntax, even for the Intel x86 and x86-64 architectures. The AT&T syntax is different to the one included in most Intel references. There are several differences, the most memorable being that two-operand instructions have the source and destinations in the opposite order. For example, instruction
mov ax, bx would be expressed in AT&T syntax as
movw %bx, %ax, i.e., the rightmost operand is the destination, and the leftmost one is the source. Other distinction is that register names used as operands must be preceded by a percent (%) sign. However, since version 2.10, Gas supports Intel syntax by means of the .intel_syntax directive. But in the following we’ll be using AT&T syntax.
Continue reading “hello world, C and GNU as”
Translation of the second line is a direct and solved issue. What about
jmp 114? Well, we want to jump over the data (18 bytes, one byte per each character in the string.) IASDM tell us (Appendix B) that the opcode for unconditional jumps in the same segment is 11101011, which in hexadecimal, is expressed as EB.
On the post Debugging hello, world, someone asked about the reason for translating the instruction
jmp 114 into hexadecimal
EB12. To answer this, we are going to recur to the “lovely” and elder Intel Architecture Software Developer Manual (IASDM), Volume 2. This volume describes the instructions set of the Intel Architecture processor (x86/IA-32) and the opcode structure. I’ll review some terms involved here:
x86: It refers to the instruction set of the Intel-compatible CPU architectures (chips produced by Intel, AMD, VIA, and others) inaugurated by Intel’s original 16-bit 8086 CPU. A decision which proved wise was to make each new instance of x86 processors almost fully backwards compatible.
IA-32: It is Intel’s 32-bit implementation of the x86 architecture; IA-32 distinguishes this implementation from the preceding 16-bit x86 processors. Note that when the 64-bit era arrived, Intel launched its Itanium processor, which discards compatibility with the IA-32 instruction set. Such 64-bit architecture description and implementation is referred to as IA-64, meaning “Intel Architecture, 64-bit”, but even though the names are similar, IA-32 and IA-64 are very different architectures and instructions sets. However, AMD’s response to Intel 64-bit processors, uses an instruction set that, in essence, is composed of 64-bit extensions to IA-32, i.e., it’s a superset of the x86 instruction set. Such instruction set is referred to as AMD64 (initially, x86-64.) Later, Intel cloned it under the name Intel 64. AMD’s processors Athlon 64, Terium, Opteron, Sempron, etc., are based on AMD64.
Opcode: An opcode (operation code) is the part of a machine language instruction (pure binary code) specifying the operation to be performed. The other portion of the instruction is the operand, which is optional and represents the data to be operated on. In assembly language, mnemonics are used to represent the opcodes. Concretely, and according to the IASDM, a mnemonic is a reserved name for a class of instruction opcodes which have the same function. For example, in
JMP 114, the mnemonic is
JMP, and the operand is 114 (remember, 114 in hexadecimal, which is 276 in decimal.)
Continue reading “Encoding Intel x86/IA-32 Assembler Instructions”
The Go command (g) will run the program starting at the given address (in this case, CS:0100) If everything goes right, the program should output the intended “hello, world!” string, and finish with the message “Program terminated normally.”
Yesterday, we took a break after long hours of intensive coding, and a coworker started establishing similarities between our current frantic coding and the (fortunately) gone days of college homework. I specifically recalled a project I had to build by using MS-DEBUG: a simple calculator in assembly, which also required the hassle of dealing with pretty and safe user input. I have no intention of looking for such listings, but I thought about revisiting, for a moment, the old and dear friend MS-DEBUG 🙂 I’ll harness a previous post in this blog, and try to build a little ‘hello, world’ program in MS-DEBUG. I know this has little value outside a personal feeling and a tad of nostalgia, maybe.
I remember that
a 100 tells DEBUG to accept code in memory starting out from
Now comes the data, which here simply consist of the string
'hello, world!'. A neat output, however, would require newlines before and after our intended string. A newline is comprised of a carriage return (CR = ASCII 13) and a line feed (LF = ASCII 10) on the display. As DEBUG only understand hexadecimal numbers, we must use 0Dh and 0Ah for CR and LF, respectively. Except in code, we will represent hexadecimal numbers by following the value with ‘h’.) Fortunately, DEBUG also admits ASCII characters directly (and the pseudo-instructions DB and DW!), so we can express our complete string as
db 0d,0a,"hello, world!",0d,0a,"$"
Continue reading “Debugging “hello, world””