我刚刚开始学习计算机和编程的基础知识。我明白了,在编译程序中,生成的机器代码是特定于处理器类型及其指令集的。我想知道的是,比如说,我有Windows、OS X和Linux都运行在完全相同的硬件上(具体来说是处理器),从这个编译程序生成的机器代码在不同的操作系统上会有所不同吗?机器代码是否依赖于操作系统,或者它将是所有操作系统中完全相同的比特和字节副本?
你试的时候发生了什么?正如所回答的那样,支持的文件格式可能会有所不同,但您询问了机器代码。
当然,相同处理器核心的机器代码是相同的。但只有一部分代码是通用的
a=b+c:
printf("%u\n",a);
假设即使您使用相同的编译器版本,目标是相同的cpu,但使用不同的操作系统(相同的计算机运行linux,然后是windows),理想情况下,添加是相同的,假设顶层函数/源代码是相同的。
非常简单的程序/函数
extern unsigned int dummy ( unsigned int );
unsigned int fun ( unsigned int a, unsigned int b )
{
dummy(a+b+3);
return(a+b+7);
}
编译后反汇编
00000000 <fun>:
0: e92d4010 push {r4, lr}
4: e0804001 add r4, r0, r1
8: e2840003 add r0, r4, #3
c: ebfffffe bl 0 <dummy>
10: e2840007 add r0, r4, #7
14: e8bd4010 pop {r4, lr}
18: e12fff1e bx lr
实际上那里有很多东西在发生。这是手臂,全尺寸(还不是拇指)。a参数在r0中出现,b在r1中出现,结果在r0中出现。lr基本上是返回地址寄存器,因此,如果我们调用另一个函数,我们需要将其保存(在堆栈中),同样,我们将重用r0来调用dummy,事实上,根据这个调用约定,任何函数都可以修改/破坏r0-r3,所以编译器需要处理我们的两个参数,因为我有意使用它们,就像编译器可以将a+B优化为寄存器,并将其保存在堆栈中一样,实际上,出于性能的原因,毫无疑问,他们将r4保存在堆栈中,然后使用r4保存a+B,你不能在基于调用约定的函数中随意修改r4,所以任何嵌套函数都必须保留它,并以as found状态返回,所以在调用其他函数时,只保留a+B是安全的。
c: ebfffffe bl 0 <dummy>
我称它为dummy是因为当我们在这里使用它时,它只返回一个dummy函数。那里编码的指令显然是错误的,分支到开始的乐趣是行不通的,那就是递归,这不是我们所要求的。所以让我们链接它,至少我们需要声明一个_starthtml" target="_blank">标签来让gnu链接器高兴,但我想做的不止这些:
.globl _start
_start
bl fun
b .
.globl dummy
dummy:
bx lr
和链接条目地址0x1000产生了
00001000 <_start>:
1000: eb000001 bl 100c <fun>
1004: eafffffe b 1004 <_start+0x4>
00001008 <dummy>:
1008: e12fff1e bx lr
0000100c <fun>:
100c: e92d4010 push {r4, lr}
1010: e0804001 add r4, r0, r1
1014: e2840003 add r0, r4, #3
1018: ebfffffa bl 1008 <dummy>
101c: e2840007 add r0, r4, #7
1020: e8bd4010 pop {r4, lr}
1024: e12fff1e bx lr
链接器通过修改调用dummy的指令来填充dummy的地址,因此您可以看到机器代码已经更改。
1018: ebfffffa bl 1008 <dummy>
.thumb
.thumb_func
.globl dummy
dummy:
bx lr
00001000 <_start>:
1000: eb000001 bl 100c <fun>
1004: eafffffe b 1004 <_start+0x4>
00001008 <dummy>:
1008: 4770 bx lr
100a: 46c0 nop ; (mov r8, r8)
0000100c <fun>:
100c: e92d4010 push {r4, lr}
1010: e0804001 add r4, r0, r1
1014: e2840003 add r0, r4, #3
1018: eb000002 bl 1028 <__dummy_from_arm>
101c: e2840007 add r0, r4, #7
1020: e8bd4010 pop {r4, lr}
1024: e12fff1e bx lr
00001028 <__dummy_from_arm>:
1028: e59fc000 ldr r12, [pc] ; 1030 <__dummy_from_arm+0x8>
102c: e12fff1c bx r12
1030: 00001009 andeq r1, r0, r9
1034: 00000000 andeq r0, r0, r0
.globl _start
_start:
bl fun
b .
.globl dummy
dummy:
bx lr
.space 0x10000000
叹息,哦,好吧
arm-none-eabi-ld -Ttext=0x1000 v.o so.o -o so.elf
v.o: In function `_start':
(.text+0x0): relocation truncated to fit: R_ARM_CALL against symbol `fun' defined in .text section in so.o
如果我们把一加改为一减:
extern unsigned int dummy ( unsigned int );
unsigned int fun ( unsigned int a, unsigned int b )
{
dummy(a-b+3);
return(a+b+7);
}
事情变得更复杂了
00000000 <fun>:
0: e92d4070 push {r4, r5, r6, lr}
4: e1a04001 mov r4, r1
8: e1a05000 mov r5, r0
c: e0400001 sub r0, r0, r1
10: e2800003 add r0, r0, #3
14: ebfffffe bl 0 <dummy>
18: e2840007 add r0, r4, #7
1c: e0800005 add r0, r0, r5
20: e8bd4070 pop {r4, r5, r6, lr}
24: e12fff1e bx lr
void dummy ( unsigned int )
{
}
unsigned int fun ( unsigned int a, unsigned int b )
{
dummy(a-b+3);
return(a+b+7);
}
00000000 <dummy>:
0: e12fff1e bx lr
00000004 <fun>:
4: e2811007 add r1, r1, #7
8: e0810000 add r0, r1, r0
c: e12fff1e bx lr
static void dummy ( unsigned int x )
{
}
unsigned int fun ( unsigned int a, unsigned int b )
{
dummy(a-b+3);
return(a+b+7);
}
00000000 <fun>:
0: e2811007 add r1, r1, #7
4: e0810000 add r0, r1, r0
8: e12fff1e bx lr
static unsigned int dummy ( unsigned int x )
{
return(x+1);
}
unsigned int fun ( unsigned int a, unsigned int b )
{
unsigned int c;
c=dummy(a-b+3);
return(a+b+c);
}
编译器检查所有操作并对其进行优化。A-B+3+1+A+B=A+A+4=(2*A)+4=(A<<1)+4;为什么他们使用shift left而不是只添加r0,r0,r0,不知道也许在管道中的shift更快,或者也许它不相关,或者两者都一样好,编译器作者选择了这个方法,或者也许内部代码有点泛型,在它进入后端之前,它已经被转换为shift而不是add。
00000000 <fun>:
0: e1a00080 lsl r0, r0, #1
4: e2800004 add r0, r0, #4
8: e12fff1e bx lr
用于这些实验的命令行
arm-none-eabi-gcc -c -O2 so.c -o so.o
arm-none-eabi-as v.s -o v.o
arm-none-eabi-ld -Ttext=0x1000 v.o so.o -o so.elf
arm-none-eabi-objdump -D so.o
arm-none-eabi-objdump -D so.elf
关键是,您可以自己做这些简单的实验,并开始了解发生了什么,编译器和链接器在何时何地修改机器代码,如果您喜欢这样想的话。然后意识到我在添加非静态虚拟函数时所展示的内容(fun()函数现在被推入内存更深处),因为您添加了更多的代码,例如,从一个操作系统到下一个操作系统的C库可能会发生变化,或者除了系统调用之外基本上相同,因此它们的大小可能会不同,导致其他代码可能被移到更大的地方,puts()可能导致printf()位于不同的地址,所有其他因素都保持不变。如果不喜欢静态,那么毫无疑问会有区别,只是在linux上查找。so文件或在windows上查找。dll的文件格式和机制解析它,将应用程序中系统调用之间的dots运行时连接到共享库。文件格式和共享库本身在应用程序空间中的位置将导致与操作特定存根链接的二进制文件不同。然后最终实际的系统调用自己。
我在操作系统或编译器优化方面没有深厚的背景,也没有直接使用机器代码,但我开始探索它。我已经开始在assembly中玩了一圈,看看像NASM这样的东西如何将汇编代码编译成机器代码(可执行文件),然后您可以从命令行调用它,比如。 但是JIT编译器实际上是如何在运行时做到这一点的呢?它是像流机器代码到stdin或什么,或者它是如何工作的?如果您能提供一个示例或一些伪代码,说明一些程序集(或类似的东西,但
如果我保存相同的代码,编译器会再次编译程序还是会留下旧的编译代码。如果它不会再次编译,意味着它如何识别它是相同的代码。
本文向大家介绍如何在Mac OS上编译和执行C#程序?,包括了如何在Mac OS上编译和执行C#程序?的使用技巧和注意事项,需要的朋友参考一下 要在Mac上编译和执行C#程序,首先需要IDE。在MacOS上,最好的IDE之一是Monodevelop。 Monodevelop是一个开放源代码的IDE,它允许您在Windows,Linux和MacOS的多个平台上运行C#。MonoDevelop也称为X
想改进这个问题吗?通过编辑这篇文章添加细节并澄清问题。 如果我保存相同的代码,编译器会再次编译程序,还是会保留旧的编译代码。如果它不再编译,意味着它如何识别它是同一个代码。
我们有一个GWT应用程序。我们使用Maven 3构建并运行GWT应用程序。当我们在Windows 7上构建和运行,并在Windows上的IE上测试时,应用程序运行良好。然而,当我们在Linux上编译和运行应用程序,然后在Windows上的IE上测试时,应用程序看起来就不同了。排除客户端问题:我们在Windows7上测试完全相同的客户端-Internet Explorer。进一步调查显示,Linux
问题内容: 我正在编译glibc库。在我可以这样做之前,我需要先跑步。但是,要编译glibc,我需要使用gcc编译器,它不是计算机上的默认编译器。该手册说明以下内容。 现在,我的问题是我对该计算机没有任何管理权限。因此,如何使用不同于默认值的编译器。 问题答案: 在linux上,任何人都可以更改其进程的环境变量。不需要管理权限。 在bash中: 在csh中使用 在该命令之后在此shell中启动的任