47

I'm considering picking up some very rudimentary understanding of assembly. My current goal is simple: VERY BASIC understanding of GCC assembler output when compiling C/C++ with the -S switch for x86/x86-64.

Just enough to do simple things such as looking at a single function and verifying whether GCC optimizes away things I expect to disappear.

Does anyone have/know of a truly concise introduction to assembly, relevant to GCC and specifically for the purpose of reading, and a list of the most important instructions anyone casually reading assembly should know?

porgarmingduod
  • 7,668
  • 10
  • 50
  • 83
  • You haven't specified what target assembly language you are interested in. Intel x86? PowerPC? – Brian Neal Apr 09 '10 at 23:10
  • See also [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/q/38552116), especially Matt Godbolt's CppCon2017 talk [“What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid”](https://youtu.be/bSkpMdDe4g4) – Peter Cordes Oct 05 '22 at 16:33

6 Answers6

31

You should use GCC's -fverbose-asm option. It makes the compiler output additional information (in the form of comments) that make it easier to understand the assembly code's relationship to the original C/C++ code.

Wyzard
  • 33,849
  • 3
  • 67
  • 87
26

If you're using gcc or clang, the -masm=intel argument tells the compiler to generate assembly with Intel syntax rather than AT&T syntax, and the --save-temps argument tells the compiler to save temporary files (preprocessed source, assembly output, unlinked object file) in the directory GCC is called from.

Getting a superficial understanding of x86 assembly should be easy with all the resources out there. Here's one such resource: http://www.cs.virginia.edu/~evans/cs216/guides/x86.html .

You can also just use disasm and gdb to see what a compiled program is doing.

Yktula
  • 14,179
  • 14
  • 48
  • 71
4

I usually hunt down the processor documentation when faced with a new device, and then just look up the opcodes as I encounter ones I don't know.

On Intel, thankfully the opcodes are somewhat sensible. PowerPC not so much in my opinion. MIPS was my favorite. For MIPS I borrowed my neighbor's little reference book, and for PPC I had some IBM documentation in a PDF that was handy to search through. (And for Intel, mostly I guess and then watch the registers to make sure I'm guessing right! heh)

Basically, the assembly itself is easy. It basically does three things: move data between memory and registers, operate on data in registers, and change the program counter. Mapping between your language of choice and the assembly will require some study (e.g. learning how to recognize a virtual function call), and for this an "integrated" source and disassembly view (like you can get in Visual Studio) is very useful.

dash-tom-bang
  • 17,383
  • 5
  • 46
  • 62
1

Unlike higher-level languages, there's really not much (if any) difference between being able to read assembly and being able to write it. Instructions have a one-to-one relationship with CPU opcodes -- there's no complexity to skip over while still retaining an understanding of what the line of code does. (It's not like a higher-level language where you can see a line that says "print $var" and not need to know or care about how it goes about outputting it to screen.)

If you still want to learn assembly, try the book Assembly Language Step-by-Step: Programming with Linux, by Jeff Duntemann.

Dan Story
  • 9,985
  • 1
  • 23
  • 27
  • 1
    I don't agree (but wouldn't downvote for that reason); it's much easier to understand something that's before you that's known to be well-formed and to create that well-formed code yourself. Being able to read assembly can certainly help /edit/ assembly, but being able to read it is a far cry from being able to author even trivial functionality from scratch. I may be able to sort of understand when people talk to me in the foreign languages that I've studied, but I sure can't speak any of them in well formed ways! – dash-tom-bang Apr 12 '10 at 18:14
0

I'm sure there are introductory books and web sites out there, but a pretty efficient way of learning it is actually to get the Intel references and then try to do simple stuff (like integer math and Boolean logic) in your favorite high-level language and then look what the resulting binary code is.

  • It is complicated a bit by GCC using AT&T syntax for its output. `MOV` isn't just called `MOV`, and the order of the operands won't always be the one listed in Intel's manuals. – Michael Madsen Apr 09 '10 at 22:36
  • 2
    If you are compiling for x86 you can use the compiler flag -masm=intel to get gcc to output assembly that looks more like Intel's manuals. – nategoose Apr 09 '10 at 22:43
0

"casually reading assembly" lol (nicely)

I would start by following in gdb at run time; you get a better feel for whats happening. But then maybe thats just me. it will disassemble a function for you (disass func) then you can single step through it

If you are doing this solely to check the optimizations - do not worry.

a) the compiler does a good job

b) you wont be able to understand what it is doing anyway (nobody can)

pm100
  • 48,078
  • 23
  • 82
  • 145
  • 7
    Sometimes I find optimized code easier to read because it notices where it's being redundant and changes it to something like I would write. – avpx Apr 09 '10 at 22:35
  • 5
    For myself, I know that it is a good idea to do this solely for checking optimizations. The reason is that every time I see the compiler actually doing something smart about *situation X*, I will not spend any time in the future *wondering*. avpx also has a very good point. – porgarmingduod Apr 09 '10 at 22:48
  • 1
    +1, that's a great idea, I've added `disass func` to a CW on gdb: http://stackoverflow.com/questions/2588853/the-community-driven-gdb-primer/2611474#2611474. By all means feel free to edit what I've put there. –  Apr 09 '10 at 22:53
  • *nobody can* - How do you think compiler developers notice missed-optimization bugs and other room for improvement in the generated asm? By reading the compiler's optimized asm output and noticing things they would have done better by hand. For complicated functions it can be too much to follow easily, but for loops that aren't huge, it's often quite possible. And as avpx says, *easier* to follow without all the store/reload noise. Many GCC and clang missed-optimization bug reports include and discuss optimized asm output. – Peter Cordes Apr 26 '23 at 15:31
  • See also [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/q/38552116) - Matt Godbolt gave a talk at CppCon2017, [“What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid”](https://youtu.be/bSkpMdDe4g4), where the compiler asm output he showed was all generated with optimization enabled. (Otherwise the answer would be "nothing".) – Peter Cordes Apr 26 '23 at 15:33
  • @PeterCordes you are right, quite often optimized code is easier to follow. I think what I meant was that following what the cpu does is really tough (it reorders code without tellling you) – pm100 Apr 28 '23 at 05:56
  • CPUs preserve the *illusion* of running your code one instruction at a time, in program order. (Except for the effects on memory observed from other cores.) So in terms of understanding what the code does and why it's correct, there's no problem. – Peter Cordes Apr 28 '23 at 06:47
  • In terms of performance, yes, out-of-order exec makes static performance analysis different from in-order CPUs but not impossible: [Predicting latency for operations on modern superscalar processors and how can I calculate?](https://stackoverflow.com/q/51607391) / [How many CPU cycles for each instruction?](https://stackoverflow.com/a/44980899) (that's the wrong question; performance has 3 dimensions: front-end cost, back-end execution-unit throughput, and latency as part of a dependency chain. One of those 3 factors will be the bottleneck for a loop without branch or cache misses.) – Peter Cordes Apr 28 '23 at 06:48