Why Do My Results Different Following Along the Tiny Asm Example

Why do my results different following along the tiny asm example?

-static is not the default even with -nostdlib when GCC is configured to make PIEs by default. Use gcc -m32 -static -nostdlib to get the historical behaviour. (-static implies -no-pie). See What's the difference between "statically linked" and "not a dynamic executable" from Linux ldd? for more.

Also, you may need to disable alignment of other sections with gcc -Wl,--nmagic or using a custom linker script, and maybe disable extra sections of metadata that GCC adds. Minimal executable size now 10x larger after linking than 2 years ago, for tiny programs?

You probably don't have a .eh_frame section if you're not linking any compiler-generated (from C) .o files. But if you were, you can disable that with gcc -fno-asynchronous-unwind-tables. (See also How to remove "noise" from GCC/clang assembly output? for general tips aimed at looking at the compiler's asm text output, moreso than executable size.)

See also GCC + LD + NDISASM = huge amount of assembler instructions (ndisasm doesn't handle metadata at all, only flat binary, so it "disassembles" metadata. So the answer there includes info on how to avoid other sections.)

GCC -Wl,--build-id=none will avoid including a .note.gnu.build-id section in the executable.

$ nasm -felf32 foo.asm
$ gcc -m32 -static -nostdlib -Wl,--build-id=none -Wl,--nmagic foo.o
$ ll a.out
-rwxr-xr-x 1 peter peter 488 Dec 26 18:47 a.out
$ strip a.out
$ ll a.out
-rwxr-xr-x 1 peter peter 248 Dec 26 18:47 a.out

(Tested on x86-64 Arch GNU/Linux, NASM 2.15.05, gcc 10.2, ld from GNU Binutils 2.35.1.)


You can check on the sections in your executable with readelf -a a.out (or use a more specific option to only get part of readelf's large output.) e.g. before stripping,

$ readelf -S unstripped_a.out
...
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 08048060 000060 00000c 00 AX 0 0 16
[ 2] .symtab SYMTAB 00000000 00006c 000070 10 3 3 4
[ 3] .strtab STRTAB 00000000 0000dc 000021 00 0 0 1
[ 4] .shstrtab STRTAB 00000000 0000fd 000021 00 0 0 1

And BTW, you definitely do not want to use nasm -felf64 on a file that uses BITS 32, unless you're writing a kernel or something that switches from 64-bit long mode to 32-bit compat mode. Putting 32-bit machine code in a 64-bit object file is not helpful. Only ever use BITS when you want raw binary mode to work (later in that tiny-ELF tutorial). When you're making a .o to link, it only makes it possible to shoot yourself in the foot; don't do it. (Although it's not harmful if you do properly use nasm -felf32 that matches your BITS directive.)

GCC + LD + NDISASM = huge amount of assembler instructions

As I concluded from @Michael Petch comments:

The binary representation of required function is represented by 00000000-0000001B lines of code snippet of the disassembled file and executes command ret at the end so the second part of the file (0000001B-00000056) is never executed - it's metadata.

As per @Michael Petch and @Jester comments:

I could figure out that the object file consists of many sections https://en.wikipedia.org/wiki/Object_file
The generated basic.o file originally had three sections:

  • .text (function itself)
  • .comment (not represented in the binary file)
  • .eh_frame

What is .eh_frame section and why GCC compiler creates it, is described here:
Why GCC compiled C program needs .eh_frame section?

By running gcc with argument -fno-asynchronous-unwind-tables I could get rid of .eh_frame section from object file.

Following is some assembly code by VS2012, how can I write it on gcc?

You can write it in gcc as:

#include <stdio.h>

int main ()
{
int a = 0 ;
/*How can I write it on gcc*/
__asm__ __volatile__ (
"movl $2, %0\n\t"
"addl $4, %0\n\t"
:"=r"(a) /* =r(egister), =m(emory) both fine here */
);
printf ("%d\n",a );
return 0 ;
}

MASM SEG operator

In MASM the label MY_VAR translates to the offset part of the address of MY_VAR relative to the segment it was declared in (if you use it like mov ax, MY_VAR) or relative the to segment you have assumed for the segment register you are using to access it (if you use it like mov ax, WORD PTR [MY_VAR]).

As you know a given variable (in general a linear address) has multiple logical address, for example the variable at 8000h linear can be accessed as 0800h:0000h or 0700h:1000h and so so.

The form MY_SEG:MY_VAR tell the assembler to compute the offset relative to the segment MY_SEG. So if MY_SEG starts at 7000h linear, MY_SEG2 starts at 6000h linear then for a var MY_VAR at 8000h linear MY_SEG:MY_VAR is 1000h and MY_SEG2:MY_VAR is 2000h.

The SEG directive compute the segment part of the logical address instead of the offset, it is the segment MASM used (again by the rules given above) to compute the offset.

In your first instructions you are telling MASM to put the address (let's leaving relocation behind) of the segment MY_SEG in AX (so if the segment starts at 5000h the value in AX is 500h).

In your second instructions your are explicit telling MASM to use the segment MY_SEG in computing the offset of MY_VAR and then, by the SEG directive, telling it to return the segment part instead, that is MY_SEG.

So they are the same but the second one is redundant.



Related Topics



Leave a reply



Submit