2016-01-30 130 views
5

我正在尝试学习汇编语言作为一种爱好,我经常使用gcc -S来生成汇编输出。这非常简单,但我无法编译程序集输出。我只是好奇这是否可以完成。我尝试使用标准汇编输出和使用-masm=intel的英特尔语法。两者都不能与nasm编译并链接到ld如何使用gcc生成汇编代码,可以使用nasm编译

因此我想问一下是否可以生成汇编代码,然后才能编译。

更确切地说,我使用了下面的C代码。

>> cat csimp.c 
int main (void){ 
int i,j; 
    for(i=1;i<21;i++) 
    j= i + 100; 
    return 0; 
    } 

生成组件与gcc -S -O0 -masm=intel csimp.c和试图与nasm -f elf64 csimp.s和链路与ld -m elf_x86_64 -s -o test csimp.o进行编译。我从nasm得到的输出如下:

csimp.s:1: error: attempt to define a local label before any non-local labels 
csimp.s:1: error: parser: instruction expected 
csimp.s:2: error: attempt to define a local label before any non-local labels 
csimp.s:2: error: parser: instruction expected 

这很可能是由于装配语法破坏造成的。我希望,我将能够解决这个问题,而不必手动纠正gcc -S


输出

编辑

我得到了我的问题是另外一个问题解决的hint;不幸的是,在测试了那里描述的方法之后,我无法制作出组装格式。您可以在下面看到objconv的输出。 因此,我仍然需要你的帮助。

>>cat csimp.asm 
; Disassembly of file: csimp.o 
; Sat Jan 30 20:17:39 2016 
; Mode: 64 bits 
; Syntax: YASM/NASM 
; Instruction set: 8086, x64 

global main: ; **the ':' should be removed !!!** 


SECTION .text           ; section number 1, code 

main: ; Function begin 
     push rbp          ; 0000 _ 55 
     mov  rbp, rsp        ; 0001 _ 48: 89. E5 
     mov  dword [rbp-4H], 1      ; 0004 _ C7. 45, FC, 00000001 
     jmp  ?_002         ; 000B _ EB, 0D 

?_001: mov  eax, dword [rbp-4H]      ; 000D _ 8B. 45, FC 
     add  eax, 100        ; 0010 _ 83. C0, 64 
     mov  dword [rbp-8H], eax      ; 0013 _ 89. 45, F8 
     add  dword [rbp-4H], 1      ; 0016 _ 83. 45, FC, 01 
?_002: cmp  dword [rbp-4H], 20      ; 001A _ 83. 7D, FC, 14 
     jle  ?_001         ; 001E _ 7E, ED 
     pop  rbp          ; 0020 _ 5D 
     ret            ; 0021 _ C3 
; main End of function 


SECTION .data           ; section number 2, data 


SECTION .bss           ; section number 3, bss 

表观解决方案:

我清理objconv输出的时候犯了一个错误。我应该已经运行:

sed -i "s/align=1//g ; s/[a-z]*execute//g ; s/: *function//g; /default *rel/d" csimp.asm 

所有步骤可以在bash脚本

#! /bin/bash 

a=$(echo $1 | sed "s/\.c//") # strip the file extension .c 

# compile binary with minimal information 
gcc -fno-asynchronous-unwind-tables -s -c ${a}.c 

# convert the executable to nasm format 
./objconv/objconv -fnasm ${a}.o 

# remove unnecesairy objconv information 
sed -i "s/align=1//g ; s/[a-z]*execute//g ; s/: *function//g; /default *rel/d" ${a}.asm 

# run nasm for 64-bit binary 

nasm -f elf64 ${a}.asm 

# link --> see comment of MichaelPetch below 
ld -m elf_x86_64 -s ${a}.o 

凝结运行这段代码中,我得到了ld警告:

ld: warning: cannot find entry symbol _start; defaulting to 0000000000400080 

以这种方式生成的可执行分段故障消息崩溃。我很感谢你的帮助。

+5

输出被用于GNU汇编('as') ,你不使用它的任何特定原因?它将“只是工作”。不幸的是,'nasm'有不同的语法。 – Jester

+0

我不知道这一点。我会尽力,谢谢你的回答。我很惊讶汇编语法不通用。 –

+0

[如何从Linux的c源代码生成nasm可编译汇编代码?](http://stackoverflow.com/questions/20737947/how-to-generate-a-nasm-compilable-assembly-code- from-c-source-code-on-linux) –

回答

3

我想你打与入口点错误的困难正试图在包含名为mainld一直在寻找一个名为_start的入口点的入口点的目标文件使用ld

有几个注意事项。首先,如果您正在使用类似printf等功能的C库链接,链接将期望main作为入口点,但如果您未链接C库,则ld将会预期为_start。您的脚本非常接近,但您需要一些方法来区分您需要为任何源文件完全自动化过程所需的入口点。

例如,以下是使用源文件的方法进行的转换,包括printf。它是用objconv转化为nasm如下:

生成对象文件:

gcc -fno-asynchronous-unwind-tables -s -c struct_offsetof.c -o s3.obj 

与objconv转换为NASM格式汇编文件

objconv -fnasm s3.obj 

(注:我的版本的objconv添加DOS行结尾 - 可能是一个选项错过了,我只是通过运行它dos2unix

使用您sed通话略加修改,调整的内容:

sed -i -e 's/align=1//g' -e 's/[a-z]*execute//g' -e \ 
's/: *function//g' -e '/default *rel/d' s3.asm 

(注:如果没有标准库函数,并使用ld,通过添加下列表达式您sed呼叫改变main_start

-e 's/^main/_start/' -e 's/[ ]main[ ]*.*$/ _start/' 

(也有可能是这更优雅的表情,这只是举例)

编译nasm(代替原来的对象文件):

nasm -felf64 -o s3.obj s3.asm 

使用gcc用于链路:

gcc -o s3 s3.obj 

测试

$ ./s3 

sizeof test : 40 

myint : 0 0 
mychar : 4 4 
myptr : 8 8 
myarr : 16 16 
myuint : 32 32 
+0

我改变了'main'开始和'ld'错误消失。但是代码仍然会产生'Segmentation fault'错误。我的代码中没有'printf',实际上它只是一个main和'for'循环,但它仍然不能运行。一般来说,如果我使用'gcc'作为链接器,一切运行都会顺利进行。问题是用'nasm'编译并用'ld'连接。 –

+0

@AlexanderCska:当然它segfaults。它试图从'_start'中'ret',而不是进行'exit(2)'系统调用。 '_start'不被任何东西调用:它是真正的入口点。 x86-64 ABI指定堆栈保存argc,* argv和* envp,而不是返回地址。它应该工作,如果你改变你的代码来调用'exit(0)'而不是'return 0',但你需要链接'libc'。所以你应该像David说的那样使用gcc链接。 IDK,如果我错过了,但你为什么这么做?一旦你编译并运行,你会开始手动修改asm吗? –

+0

如果您想直接使用系统调用*,而不经过glibc包装器,那么就会有像'_syscall1(type,name,type1,arg1)'这样的宏定义一个内联函数来进行系统调用。见'_syscall(2)'。或者你可以修改'call'指令的asm来将参数放在系统调用的正确寄存器中,而不是调用函数,并使用'syscall'。它破坏了rax,rcx和r11:请参阅http://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-x86-64 –

3

有许多不同的汇编语言 - 对于每个CPU,可能有多种可能的语法(例如“Intel语法”,“AT语法”),然后是完全不同的指令,预处理器等等。它仅为32位80x86添加了大约30种不同的汇编语言方言。

GCC只能为32位80x86生成一种汇编语言方言。这意味着它不能与NASM,FASM,MASM,TASM,A86/A386等一起工作。它只适用于GAS(也可能在其“AT & T模式”中可能有YASM)。当然,你可以用3种不同的编译器将代码编译成3种不同类型的程序集,然后自己编写3种不同类型的代码(在3种不同类型的程序集中)然后将所有这些(每个都带有合适的汇编程序)汇编到目标文件中,并将所有目标文件链接在一起。

2

你基本上不能,至少直接。 GCC确实以英特尔语法输出汇编;但NASM/MASM/TASM拥有自己的英特尔语法。它们主要基于它,但汇编程序可能无法理解并因此无法编译出现一些差异。

最接近的可能是有objdump显示Intel格式的组件:

objdump -d $file -M intel 

彼得·科德斯建议在评论认为汇编指令仍将目标气体,所以他们不会被NASM例如认可。它们通常具有相同的名称,但类似GAS的指令以(与section text)中的.开头。

+0

另请参阅:https://stackoverflow.com/questions/8406188/does-gcc-really-know-how-to-output-nasm-assembly?lq=1 – edmz

+1

gcc/gas Intel语法仍然使用GNU汇编程序指令,例如' .align','.globl',而NASM/YASM使用像'align'和'global'这样的指令。所以你必须手工移植。 –

+0

@PeterCordes是的,那是真的。 GCC“限制”自己告诉GAS通过另一个类似GAS的指令'.intel_syntax'切换语法。 – edmz

相关问题