To create assembly code I'm calling the mipsel-openwrt-linux-uclibc-gcc compiler (Linaro GCC 4.8-2014.04 r49389) with flag -S.
This produces assembly code looking like this:
.section .mdebug.abi32
.previous
.gnu_attribute 4, 3
.abicalls
.option pic0
.text
$Ltext0:
.cfi_sections .debug_frame
.globl Version
.section .rodata.str1.4,"aMS",#progbits,1
.align 2
$LC0:
.ascii "3.3\000"
.data
.align 2
.type Version, #object
.size Version, 4
Version:
.word $LC0
.text
$Letext0:
.file 1 "version.c"
.section .debug_info,"",#progbits
$Ldebug_info0:
.4byte 0x3d
.2byte 0x4
.4byte $Ldebug_abbrev0
.byte 0x4
.uleb128 0x1
.4byte $LASF0
.byte 0x1
.4byte $LASF1
.4byte $LASF2
.4byte $Ldebug_line0
.uleb128 0x2
.4byte $LASF3
.byte 0x1
.byte 0x2
.4byte 0x2e
.uleb128 0x5
.byte 0x3
.4byte Version
.uleb128 0x3
.byte 0x4
.4byte 0x34
.uleb128 0x4
.4byte 0x39
.uleb128 0x5
.byte 0x1
.byte 0x6
.4byte $LASF4
.byte 0
.section .debug_abbrev,"",#progbits
$Ldebug_abbrev0:
.uleb128 0x1
.uleb128 0x11
.byte 0x1
---- I cutted here ----
.uleb128 0xe
.byte 0
.byte 0
.byte 0
.section .debug_aranges,"",#progbits
.4byte 0x14
.2byte 0x2
.4byte $Ldebug_info0
.byte 0x4
.byte 0
.2byte 0
.2byte 0
.4byte 0
.4byte 0
.section .debug_line,"",#progbits
$Ldebug_line0:
.section .debug_str,"MS",#progbits,1
$LASF2:
.ascii "/home/lvr/Src/openwrt/build_dir/target-mipsel_mips32_uCl"
.ascii "ibc-0.9.33.2/diffutils-3.3/src\000"
$LASF0:
.ascii "GNU C 4.8.3 -mno-branch-likely -mips32 -mtune=mips32 -ms"
.ascii "oft-float -mllsc -mplt -mno-shared -g -Os -std=c99 -fno-"
.ascii "caller-saves -fhonour-copts\000"
$LASF3:
.ascii "Version\000"
$LASF4:
.ascii "char\000"
$LASF1:
.ascii "version.c\000"
.ident "GCC: (OpenWrt/Linaro GCC 4.8-2014.04 r49389) 4.8.3"
.section .note.GNU-stack,"",#progbits
This doesn't look like AT&T nor Intel syntax to me. To produce more familiar syntax I've tried the -masm=intel flag, but same effect..
My first thougt was the produced ASM code is dedicated to the fixed word width of mips,
but any ideas how to generate AT&T or Intel syntax via mipsel-openwrt-linux-uclibc-gcc?
Thanks for any hints!
"What AT&T and Intel syntax have to do with MIPS?" - Nothing directly, I've tagged it that way as tag 'mipsel-openwrt-linux-uclibc-gcc' was not available (sry, maybe bad style).
But thanks Margaret your comment directed me to compile some more .c files leading to the expected AT&T syntax :) - f.e.:
.section .mdebug.abi32
.previous
.gnu_attribute 4, 3
.abicalls
.option pic0
.text
$Ltext0:
.cfi_sections .debug_frame
.align 2
$LFB16 = .
.file 1 "io.c"
.loc 1 474 0
.cfi_startproc
.set nomips16
.ent prepare_text
.type prepare_text, #function
prepare_text:
.frame $sp,48,$31 # vars= 0, regs= 5/0, args= 16, gp= 8
.mask 0x800f0000,-4
.fmask 0x00000000,0
.set noreorder
.set nomacro
$LVL0 = .
addiu $sp,$sp,-48
.cfi_def_cfa_offset 48
sw $17,32($sp)
sw $31,44($sp)
sw $19,40($sp)
sw $18,36($sp)
sw $16,28($sp)
.cfi_offset 17, -16
.cfi_offset 31, -4
.cfi_offset 19, -8
.cfi_offset 18, -12
--- CUT ---
$LASF16:
.ascii "__ino64_t\000"
$LASF86:
.ascii "changes\000"
$LASF66:
.ascii "__ctype_touplow_t\000"
$LASF200:
.ascii "memcmp\000"
$LASF118:
.ascii "equivs\000"
$LASF139:
.ascii "file_size\000"
.ident "GCC: (OpenWrt/Linaro GCC 4.8-2014.04 r49389) 4.8.3"
.section .note.GNU-stack,"",#progbits
Related
I'm playing with GAS assembly and linking it without using gcc. Instead I'm using the as assembler and linking with ld.
My code:
.section .text
.globl _start
_start:
xorq %rax, %rax
movb $60, %al
movb $1, %dil
int $0x80
The command I use to assemble and link:
as -g -D -o test.o test.S && ld --format elf64-x86-64 -o test test.o
And this is the gdb debugging before/after:
(gdb) disas _start
Dump of assembler code for function _start:
0x0000000000400078 <+0>: xor %rax,%rax
0x000000000040007b <+3>: mov $0x3c,%al
0x000000000040007d <+5>: mov $0x1,%dil
0x0000000000400080 <+8>: int $0x80
End of assembler dump.
(gdb) b *_start+8
Breakpoint 1 at 0x400080: file test.S, line 7.
(gdb) r
Starting program: /tmp/test
Breakpoint 1, _start () at test.S:7
7 int $0x80
(gdb) i r
rax 0x3c 60
rbx 0x0 0
rcx 0x0 0
rdx 0x0 0
rsi 0x0 0
rdi 0x1 1
rbp 0x0 0x0
rsp 0x7fffffffebc0 0x7fffffffebc0
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0x0 0
r12 0x0 0
r13 0x0 0
r14 0x0 0
r15 0x0 0
rip 0x400080 0x400080 <_start+8>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400082 in ?? ()
(gdb) i r
rax 0x12 18
rbx 0x0 0
rcx 0x0 0
rdx 0x0 0
rsi 0x0 0
rdi 0x1 1
rbp 0x0 0x0
rsp 0x7fffffffebc0 0x7fffffffebc0
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0x0 0
r12 0x0 0
r13 0x0 0
r14 0x0 0
r15 0x0 0
rip 0x400082 0x400082
eflags 0x10246 [ PF ZF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/x $rip
0x400082: 0x00000000
(gdb) x/x $rip-2
0x400080 <_start+8>: 0x000080cd
%rip is continuing with his trip through the code to 0x40082 where no code exists. Any idea how can I avoid the SEGFAULT after executing exit() syscall?
Add
Stracing the program:
$ strace ./test
execve("./test", ["./test"], [/* 18 vars */]) = 0
exit(1) = 18
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x12} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
I am linking my assembly function with GCC on linux 64-bit. The library I use is TAN from math.h. I link it with;
gcc -s prog.o -o prog -lm
The program works but the return value is 0.0000000 (for 3.4 radian). I use extrn in my assembly code;
extrn tan
extrn printf
I use xmm0 to pass the argument (in radian) to the TAN function. Now I am not sure which register is used to return the value from TAN. Is it xmm0, st0 or in RAX? I can't find a decent reference on this.
For my gcc, it's xmm0.
Here's a C program:
#include <stdio.h>
#include <math.h>
int main () {
double x = tan(M_PI/4.0);
// RESULT: x=1.000000
printf ("x=%f\n", x);
return 0;
}
And here's the corresponding "gcc -S":
.Ltext0:
.section .rodata
.LC1:
.string "x=%f\n"
.text
.globl main
.type main, #function
main:
.LFB0:
.file 1 "x.cpp"
.loc 1 4 0
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
.LBB2:
.loc 1 6 0
movabsq $4607182418800017407, %rax
movq %rax, -8(%rbp)
.loc 1 8 0
movq -8(%rbp), %rax
movq %rax, -24(%rbp)
movsd -24(%rbp), %xmm0
movl $.LC1, %edi
movl $1, %eax
call printf
.loc 1 9 0
movl $0, %eax
.LBE2:
.loc 1 10 0
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
I am interested in programming in x86-64 assembly on the Mac OS X platform. I came across this page about creating a 248B Mach-O program, which led me to Apple's own Mach-O format reference. After that I thought I'd make that same simple C program in Xcode and check out the generated assembly.
This was the code:
int main(int argc, const char * argv[])
{
return 42;
}
But the assembly generated was 334 lines, containing (based on the 248B model) a lot of excess content.
Firstly, why is so much DWARF debug info included in the Release build of a C executable? Secondly, I notice the Mach-O header data is included 4 times (in different DWARF-related sections). Why is this necessary? Finally, the Xcode assembly includes:
.private_extern _main
.globl _main
_main:
.cfi_startproc
But in the 248B program, these are all nowhere to be seen - the program instead begins at _start. How is that possible if all programs by definition begin in main?
Full Xcode Assembly:
# Assembly output for main.c
# Generated at 4:04:08 PM on Sunday, January 20, 2013
# Using Release configuration, x86_64 architecture for Tiny target of Tiny project
.section __TEXT,__text,regular,pure_instructions
.file 1 "/Users/####/Desktop/Tiny/Tiny/main.c"
.section __DWARF,__debug_info,regular,debug
Lsection_info:
.section __DWARF,__debug_abbrev,regular,debug
Lsection_abbrev:
.section __DWARF,__debug_aranges,regular,debug
.section __DWARF,__debug_macinfo,regular,debug
.section __DWARF,__debug_line,regular,debug
Lsection_line:
.section __DWARF,__debug_loc,regular,debug
.section __DWARF,__debug_pubtypes,regular,debug
.section __DWARF,__debug_str,regular,debug
Lsection_str:
.section __DWARF,__debug_ranges,regular,debug
Ldebug_range:
.section __DWARF,__debug_loc,regular,debug
Lsection_debug_loc:
.section __TEXT,__text,regular,pure_instructions
Ltext_begin:
.section __DATA,__data
.section __TEXT,__text,regular,pure_instructions
.private_extern _main
.globl _main
_main: ## #main
.cfi_startproc
Lfunc_begin0:
.loc 1 12 0 ## /Users/####/Desktop/Tiny/Tiny/main.c:12:0
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
##DEBUG_VALUE: main:argc <- EDI+0
##DEBUG_VALUE: main:argv <- RSI+0
movl $42, %eax
.loc 1 15 5 prologue_end ## /Users/####/Desktop/Tiny/Tiny/main.c:15:5
Ltmp5:
popq %rbp
ret
Ltmp6:
Lfunc_end0:
.cfi_endproc
Ltext_end:
.section __DATA,__data
Ldata_end:
.section __TEXT,__text,regular,pure_instructions
Lsection_end1:
.section __DWARF,__debug_info,regular,debug
Linfo_begin1:
.long 127 ## Length of Compilation Unit Info
.short 2 ## DWARF version number
Lset0 = Labbrev_begin-Lsection_abbrev ## Offset Into Abbrev. Section
.long Lset0
.byte 8 ## Address Size (in bytes)
.byte 1 ## Abbrev [1] 0xb:0x78 DW_TAG_compile_unit
Lset1 = Lstring0-Lsection_str ## DW_AT_producer
.long Lset1
.short 12 ## DW_AT_language
Lset2 = Lstring1-Lsection_str ## DW_AT_name
.long Lset2
.quad 0 ## DW_AT_entry_pc
.long 0 ## DW_AT_stmt_list
Lset3 = Lstring2-Lsection_str ## DW_AT_comp_dir
.long Lset3
.byte 1 ## DW_AT_APPLE_optimized
.byte 2 ## Abbrev [2] 0x27:0x3e DW_TAG_subprogram
Lset4 = Lstring3-Lsection_str ## DW_AT_name
.long Lset4
.byte 1 ## DW_AT_decl_file
.byte 11 ## DW_AT_decl_line
.byte 1 ## DW_AT_prototyped
.long 101 ## DW_AT_type
.byte 1 ## DW_AT_external
.quad Lfunc_begin0 ## DW_AT_low_pc
.quad Lfunc_end0 ## DW_AT_high_pc
.byte 1 ## DW_AT_frame_base
.byte 86
.byte 3 ## Abbrev [3] 0x46:0xf DW_TAG_formal_parameter
Lset5 = Lstring5-Lsection_str ## DW_AT_name
.long Lset5
.byte 1 ## DW_AT_decl_file
.byte 11 ## DW_AT_decl_line
.long 101 ## DW_AT_type
Lset6 = Ldebug_loc0-Lsection_debug_loc ## DW_AT_location
.long Lset6
.byte 3 ## Abbrev [3] 0x55:0xf DW_TAG_formal_parameter
Lset7 = Lstring6-Lsection_str ## DW_AT_name
.long Lset7
.byte 1 ## DW_AT_decl_file
.byte 11 ## DW_AT_decl_line
.long 125 ## DW_AT_type
Lset8 = Ldebug_loc2-Lsection_debug_loc ## DW_AT_location
.long Lset8
.byte 0 ## End Of Children Mark
.byte 4 ## Abbrev [4] 0x65:0x7 DW_TAG_base_type
Lset9 = Lstring4-Lsection_str ## DW_AT_name
.long Lset9
.byte 5 ## DW_AT_encoding
.byte 4 ## DW_AT_byte_size
.byte 4 ## Abbrev [4] 0x6c:0x7 DW_TAG_base_type
Lset10 = Lstring7-Lsection_str ## DW_AT_name
.long Lset10
.byte 6 ## DW_AT_encoding
.byte 1 ## DW_AT_byte_size
.byte 5 ## Abbrev [5] 0x73:0x5 DW_TAG_const_type
.long 108 ## DW_AT_type
.byte 6 ## Abbrev [6] 0x78:0x5 DW_TAG_pointer_type
.long 115 ## DW_AT_type
.byte 6 ## Abbrev [6] 0x7d:0x5 DW_TAG_pointer_type
.long 120 ## DW_AT_type
.byte 0 ## End Of Children Mark
Linfo_end1:
.section __DWARF,__debug_abbrev,regular,debug
Labbrev_begin:
.byte 1 ## Abbreviation Code
.byte 17 ## DW_TAG_compile_unit
.byte 1 ## DW_CHILDREN_yes
.byte 37 ## DW_AT_producer
.byte 14 ## DW_FORM_strp
.byte 19 ## DW_AT_language
.byte 5 ## DW_FORM_data2
.byte 3 ## DW_AT_name
.byte 14 ## DW_FORM_strp
.byte 82 ## DW_AT_entry_pc
.byte 1 ## DW_FORM_addr
.byte 16 ## DW_AT_stmt_list
.byte 6 ## DW_FORM_data4
.byte 27 ## DW_AT_comp_dir
.byte 14 ## DW_FORM_strp
.ascii "\341\177" ## DW_AT_APPLE_optimized
.byte 12 ## DW_FORM_flag
.byte 0 ## EOM(1)
.byte 0 ## EOM(2)
.byte 2 ## Abbreviation Code
.byte 46 ## DW_TAG_subprogram
.byte 1 ## DW_CHILDREN_yes
.byte 3 ## DW_AT_name
.byte 14 ## DW_FORM_strp
.byte 58 ## DW_AT_decl_file
.byte 11 ## DW_FORM_data1
.byte 59 ## DW_AT_decl_line
.byte 11 ## DW_FORM_data1
.byte 39 ## DW_AT_prototyped
.byte 12 ## DW_FORM_flag
.byte 73 ## DW_AT_type
.byte 19 ## DW_FORM_ref4
.byte 63 ## DW_AT_external
.byte 12 ## DW_FORM_flag
.byte 17 ## DW_AT_low_pc
.byte 1 ## DW_FORM_addr
.byte 18 ## DW_AT_high_pc
.byte 1 ## DW_FORM_addr
.byte 64 ## DW_AT_frame_base
.byte 10 ## DW_FORM_block1
.byte 0 ## EOM(1)
.byte 0 ## EOM(2)
.byte 3 ## Abbreviation Code
.byte 5 ## DW_TAG_formal_parameter
.byte 0 ## DW_CHILDREN_no
.byte 3 ## DW_AT_name
.byte 14 ## DW_FORM_strp
.byte 58 ## DW_AT_decl_file
.byte 11 ## DW_FORM_data1
.byte 59 ## DW_AT_decl_line
.byte 11 ## DW_FORM_data1
.byte 73 ## DW_AT_type
.byte 19 ## DW_FORM_ref4
.byte 2 ## DW_AT_location
.byte 6 ## DW_FORM_data4
.byte 0 ## EOM(1)
.byte 0 ## EOM(2)
.byte 4 ## Abbreviation Code
.byte 36 ## DW_TAG_base_type
.byte 0 ## DW_CHILDREN_no
.byte 3 ## DW_AT_name
.byte 14 ## DW_FORM_strp
.byte 62 ## DW_AT_encoding
.byte 11 ## DW_FORM_data1
.byte 11 ## DW_AT_byte_size
.byte 11 ## DW_FORM_data1
.byte 0 ## EOM(1)
.byte 0 ## EOM(2)
.byte 5 ## Abbreviation Code
.byte 38 ## DW_TAG_const_type
.byte 0 ## DW_CHILDREN_no
.byte 73 ## DW_AT_type
.byte 19 ## DW_FORM_ref4
.byte 0 ## EOM(1)
.byte 0 ## EOM(2)
.byte 6 ## Abbreviation Code
.byte 15 ## DW_TAG_pointer_type
.byte 0 ## DW_CHILDREN_no
.byte 73 ## DW_AT_type
.byte 19 ## DW_FORM_ref4
.byte 0 ## EOM(1)
.byte 0 ## EOM(2)
.byte 0 ## EOM(3)
Labbrev_end:
.section __DWARF,__apple_names,regular,debug
Lnames_begin:
.long 1212240712 ## Header Magic
.short 1 ## Header Version
.short 0 ## Header Hash Function
.long 1 ## Header Bucket Count
.long 1 ## Header Hash Count
.long 12 ## Header Data Length
.long 0 ## HeaderData Die Offset Base
.long 1 ## HeaderData Atom Count
.short 1 ## eAtomTypeDIEOffset
.short 6 ## DW_FORM_data4
.long 0 ## Bucket 0
.long 2090499946 ## Hash in Bucket 0
.long LNames0-Lnames_begin ## Offset in Bucket 0
LNames0:
Lset11 = Lstring3-Lsection_str ## main
.long Lset11
.long 1 ## Num DIEs
.long 39
.long 0
.section __DWARF,__apple_objc,regular,debug
Lobjc_begin:
.long 1212240712 ## Header Magic
.short 1 ## Header Version
.short 0 ## Header Hash Function
.long 1 ## Header Bucket Count
.long 0 ## Header Hash Count
.long 12 ## Header Data Length
.long 0 ## HeaderData Die Offset Base
.long 1 ## HeaderData Atom Count
.short 1 ## eAtomTypeDIEOffset
.short 6 ## DW_FORM_data4
.long -1 ## Bucket 0
.section __DWARF,__apple_namespac,regular,debug
Lnamespac_begin:
.long 1212240712 ## Header Magic
.short 1 ## Header Version
.short 0 ## Header Hash Function
.long 1 ## Header Bucket Count
.long 0 ## Header Hash Count
.long 12 ## Header Data Length
.long 0 ## HeaderData Die Offset Base
.long 1 ## HeaderData Atom Count
.short 1 ## eAtomTypeDIEOffset
.short 6 ## DW_FORM_data4
.long -1 ## Bucket 0
.section __DWARF,__apple_types,regular,debug
Ltypes_begin:
.long 1212240712 ## Header Magic
.short 1 ## Header Version
.short 0 ## Header Hash Function
.long 2 ## Header Bucket Count
.long 2 ## Header Hash Count
.long 20 ## Header Data Length
.long 0 ## HeaderData Die Offset Base
.long 3 ## HeaderData Atom Count
.short 1 ## eAtomTypeDIEOffset
.short 6 ## DW_FORM_data4
.short 3 ## eAtomTypeTag
.short 5 ## DW_FORM_data2
.short 5 ## eAtomTypeTypeFlags
.short 11 ## DW_FORM_data1
.long 0 ## Bucket 0
.long 1 ## Bucket 1
.long 193495088 ## Hash in Bucket 0
.long 2090147939 ## Hash in Bucket 1
.long Ltypes0-Ltypes_begin ## Offset in Bucket 0
.long Ltypes1-Ltypes_begin ## Offset in Bucket 1
Ltypes0:
Lset12 = Lstring4-Lsection_str ## int
.long Lset12
.long 1 ## Num DIEs
.long 101
.short 36
.byte 0
.long 0
Ltypes1:
Lset13 = Lstring7-Lsection_str ## char
.long Lset13
.long 1 ## Num DIEs
.long 108
.short 36
.byte 0
.long 0
.section __DWARF,__debug_pubtypes,regular,debug
Lset14 = Lpubtypes_end1-Lpubtypes_begin1 ## Length of Public Types Info
.long Lset14
Lpubtypes_begin1:
.short 2 ## DWARF Version
Lset15 = Linfo_begin1-Lsection_info ## Offset of Compilation Unit Info
.long Lset15
Lset16 = Linfo_end1-Linfo_begin1 ## Compilation Unit Length
.long Lset16
.long 0 ## End Mark
Lpubtypes_end1:
.section __DWARF,__debug_loc,regular,debug
Ldebug_loc0:
.quad Lfunc_begin0
.quad Ltmp6
Lset17 = Ltmp8-Ltmp7 ## Loc expr size
.short Lset17
Ltmp7:
.byte 85 ## DW_OP_reg5
Ltmp8:
.quad 0
.quad 0
Ldebug_loc2:
.quad Lfunc_begin0
.quad Ltmp6
Lset18 = Ltmp10-Ltmp9 ## Loc expr size
.short Lset18
Ltmp9:
.byte 84 ## DW_OP_reg4
Ltmp10:
.quad 0
.quad 0
Ldebug_loc4:
.section __DWARF,__debug_aranges,regular,debug
.section __DWARF,__debug_ranges,regular,debug
.section __DWARF,__debug_macinfo,regular,debug
.section __DWARF,__debug_inlined,regular,debug
Lset19 = Ldebug_inlined_end1-Ldebug_inlined_begin1 ## Length of Debug Inlined Information Entry
.long Lset19
Ldebug_inlined_begin1:
.short 2 ## Dwarf Version
.byte 8 ## Address Size (in bytes)
Ldebug_inlined_end1:
.section __DWARF,__debug_str,regular,debug
Lstring0:
.asciz "Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn)"
Lstring1:
.asciz "/Users/####/Desktop/Tiny/Tiny/main.c"
Lstring2:
.asciz "/Users/####/Desktop/Tiny"
Lstring3:
.asciz "main"
Lstring4:
.asciz "int"
Lstring5:
.asciz "argc"
Lstring6:
.asciz "argv"
Lstring7:
.asciz "char"
.subsections_via_symbols
Firstly, why is so much DWARF debug info included in the Release build of a C executable?
Being able to debug optimized code is incredibly useful. Cases in which bugs are only visible in optimized builds are not rare. If you're hand writing assembly you're unlikely to care about DWARF information though, so I'd suggest building your comparison code without the -g argument.
Secondly, I notice the Mach-O header data is included 4 times (in different DWARF-related sections). Why is this necessary?
These aren't Mach-O headers that you're seeing. They're the headers for DWARF accelerator tables, an LLVM extension to DWARF that optimizes the test for whether a symbol is defined within a given compilation unit.
But in the 248B program, these are all nowhere to be seen - the program instead begins at _start. How is that possible if all programs by definition begin in main?
Historically on OS X all programs begin at start. However, this symbol typically comes from a system library rather than being defined by the program itself. The system implementation of start will perform some initialization and then jump to your programs "real" entry point.
The entry points to Mach-O binaries is defined by either the LC_UNIXTHREAD or LC_MAIN load commands. When LC_UNIXTHREAD, the convention for pre-10.8 versions of OS X, is used with a regular C or C++ program the linker uses start as the entry point. This symbol typically comes from /usr/lib/crt1.o, and its address is written in to the instruction pointer field of the LC_UNIXTHREAD load command. The 248B binary you link to includes an LC_UNIXTHREAD command with eip set to 0x000010e8. That's the address of the symbol _start. Since this small program is a static executable and the binary is generated directly it can write whatever address it wishes to in to the instruction pointer field of the load command.
If you're building your executable targeting OS X 10.8+ the linker will generate an LC_MAIN load command instead of LC_UNIXTHREAD. The kernel knows that binaries using the LC_MAIN command should be executed by loading the dynamic linker and jumping to its entry point. The dynamic linker, dyld, initializes itself and then jumps to the address specified in the LC_MAIN command. In this brave new world no symbol named start is used at all.
(I don't know almost anything about assembly language yet).
I'm trying to follow this tutorial.
The problem is that his compiler, and my test setup (gcc on Linux 32 bit) produces completely different, and significantly less output than my main setup (clang on OSX 64 bit).
Here are my outputs for int main() {}
gcc on Linux 32 bit
$ cat blank.c
int main() {}
$ gcc -S blank.c
$ cat blank.s
.file "blank.c"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
popl %ebp
.cfi_def_cfa 4, 4
.cfi_restore 5
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",#progbits
clang on Mac OSX 64 bit
$ cat blank.c
int main() {}
$ clang -S blank.c
$ cat blank.s
.section __TEXT,__text,regular,pure_instructions
.globl _main
.align 4, 0x90
_main: ## #main
Leh_func_begin0:
## BB#0:
pushq %rbp
Ltmp0:
movq %rsp, %rbp
Ltmp1:
movl $0, %eax
popq %rbp
ret
Leh_func_end0:
.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
EH_frame0:
Lsection_eh_frame0:
Leh_frame_common0:
Lset0 = Leh_frame_common_end0-Leh_frame_common_begin0 ## Length of Common Information Entry
.long Lset0
Leh_frame_common_begin0:
.long 0 ## CIE Identifier Tag
.byte 1 ## DW_CIE_VERSION
.asciz "zR" ## CIE Augmentation
.byte 1 ## CIE Code Alignment Factor
.byte 120 ## CIE Data Alignment Factor
.byte 16 ## CIE Return Address Column
.byte 1 ## Augmentation Size
.byte 16 ## FDE Encoding = pcrel
.byte 12 ## DW_CFA_def_cfa
.byte 7 ## Register
.byte 8 ## Offset
.byte 144 ## DW_CFA_offset + Reg (16)
.byte 1 ## Offset
.align 3
Leh_frame_common_end0:
.globl _main.eh
_main.eh:
Lset1 = Leh_frame_end0-Leh_frame_begin0 ## Length of Frame Information Entry
.long Lset1
Leh_frame_begin0:
Lset2 = Leh_frame_begin0-Leh_frame_common0 ## FDE CIE offset
.long Lset2
Ltmp2: ## FDE initial location
Ltmp3 = Leh_func_begin0-Ltmp2
.quad Ltmp3
Lset3 = Leh_func_end0-Leh_func_begin0 ## FDE address range
.quad Lset3
.byte 0 ## Augmentation size
.byte 4 ## DW_CFA_advance_loc4
Lset4 = Ltmp0-Leh_func_begin0
.long Lset4
.byte 14 ## DW_CFA_def_cfa_offset
.byte 16 ## Offset
.byte 134 ## DW_CFA_offset + Reg (6)
.byte 2 ## Offset
.byte 4 ## DW_CFA_advance_loc4
Lset5 = Ltmp1-Ltmp0
.long Lset5
.byte 13 ## DW_CFA_def_cfa_register
.byte 6 ## Register
.align 3
Leh_frame_end0:
.subsections_via_symbols
Is it possible to generate similar assembly output on my Mac, so I can follow the tutorial? or is this assembly code platform-specific? And if it is, what flags on clang can I use to generate less verbose/boilerplate(?) code?
Make sure you instruct clang to generate 32 bit code with clang -m32 on Mac OSX 64 bit and you basically don't have to worry about the other differences.
Both the .cfi_XXX directives in the gcc output and the lines after .section __TEXT,__eh_frame in the clang output are used to generate the .eh_frame section for stack unwinding. For details, see: http://blog.mozilla.org/respindola/2011/05/12/cfi-directives/
Compile your program with gcc -fno-asynchronous-unwind-tables. Or just ignore various .cfi_XYZ directives. For the clang case, just don't pay attention to the __eh_frame section. Bear in mind that it's rather uncommon for two different compilers to generate identical code, even from identical source.
This question already has answers here:
How to remove "noise" from GCC/clang assembly output?
(3 answers)
Closed 6 years ago.
I've been teaching myself GNU Assembly for a while now by writing statements in C, compiling them with "gcc -S" and studying the output. This works alright on x86 (and compiling with -m32) but on my AMD64 box, for this code (just as an example):
int main()
{
return 0;
}
GCC gives me:
.file "test.c"
.text
.globl main
.type main, #function
main:
.LFB2:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl $0, %eax
leave
ret
.LFE2:
.size main, .-main
.section .eh_frame,"a",#progbits
.Lframe1:
.long .LECIE1-.LSCIE1
.LSCIE1:
.long 0x0
.byte 0x1
.string "zR"
.uleb128 0x1
.sleb128 -8
.byte 0x10
.uleb128 0x1
.byte 0x3
.byte 0xc
.uleb128 0x7
.uleb128 0x8
.byte 0x90
.uleb128 0x1
.align 8
.LECIE1:
.LSFDE1:
.long .LEFDE1-.LASFDE1
.LASFDE1:
.long .LASFDE1-.Lframe1
.long .LFB2
.long .LFE2-.LFB2
.uleb128 0x0
.byte 0x4
.long .LCFI0-.LFB2
.byte 0xe
.uleb128 0x10
.byte 0x86
.uleb128 0x2
.byte 0x4
.long .LCFI1-.LCFI0
.byte 0xd
.uleb128 0x6
.align 8
.LEFDE1:
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",#progbits
Compared with:
.file "test.c"
.text
.globl main
.type main, #function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
movl $0, %eax
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",#progbits
on x86.
Is there a way to make GCC -S on x86_64 output Assembly without the fluff?
The stuff that goes into .eh_frame section is unwind descriptors, which you only need to unwind stack (e.g. with GDB). While learning assembly, you could simply ignore it. Here is a way to do the "clean up" you want:
gcc -S -o - test.c | sed -e '/^\.L/d' -e '/\.eh_frame/Q'
.file "test.c"
.text
.globl main
.type main,#function
main:
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
leave
ret
.size main,.Lfe1-main
You can try placing the code you want to study in a function.
E.g.:
int ftest(void)
{
return 0;
}
int main(void)
{
return ftest();
}
If you look at the assembly-source for test it will be as clean as you need.
..snip..
test:
.LFB2:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl $0, %eax
leave
ret
..snip..
I've found that using the -Os flag makes things clearer. I tried it your tiny example, but it made very little difference.
That said, I remember it being helpful when I was learning assembly (on a Sparc).