10

gcc で生成された .o ファイルに対して tcc (ver 0.9.26) でソースをコンパイルしようとしていますが、奇妙な動作をします。gcc (ver 5.3.0) は MinGW 64 ビットのものです。

具体的には、次の 2 つのファイル (te1.c te2.c) があります。Windows7ボックスで次のコマンドを実行しました

c:\tcc> gcc -c te1.c
c:\tcc> objcopy -O  elf64-x86-64 te1.o   #this is needed because te1.o from previous step is in COFF format, tcc only understand ELF format
c:\tcc> tcc te2.c te1.o
c:\tcc> te2.exe
567in dummy!!!

文字列から 4 バイトが切り取られていることに注意してください1234567in dummy!!!\n。何がうまくいかなかったのだろうか。

ありがとうジン

========ファイル te1.c===========

#include <stdio.h>

void dummy () {
    printf1("1234567in dummy!!!\n");
}

========ファイル te2.c===========

#include <stdio.h>

void printf1(char *p) {
    printf("%s\n",p);
}
extern void dummy();
int main(int argc, char *argv[]) {
    dummy();
    return 0;
}

更新 1

te1.o (tcc でコンパイルされた te1.c) と te1_gcc.o (gcc でコンパイルされた te1.c) の間のアセンブリの違いを見ました。コンパイルされた tcc では が見られlea -0x4(%rip),%rcx、コンパイルされた gcc では が見られましたlea 0x0(%rip),%rcx。理由がわからない。

C:\temp>objdump -d te1.o

te1.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <dummy>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 81 ec 20 00 00 00    sub    $0x20,%rsp
   b:   48 8d 0d fc ff ff ff    lea    -0x4(%rip),%rcx        # e <dummy+0xe>
  12:   e8 fc ff ff ff          callq  13 <dummy+0x13>
  17:   c9                      leaveq
  18:   c3                      retq
  19:   00 00                   add    %al,(%rax)
  1b:   00 01                   add    %al,(%rcx)
  1d:   04 02                   add    $0x2,%al
  1f:   05 04 03 01 50          add    $0x50010304,%eax

C:\temp>objdump -d te1_gcc.o

te1_gcc.o:     file format pe-x86-64


Disassembly of section .text:

0000000000000000 <dummy>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 20             sub    $0x20,%rsp
   8:   48 8d 0d 00 00 00 00    lea    0x0(%rip),%rcx        # f <dummy+0xf>
   f:   e8 00 00 00 00          callq  14 <dummy+0x14>
  14:   90                      nop
  15:   48 83 c4 20             add    $0x20,%rsp
  19:   5d                      pop    %rbp
  1a:   c3                      retq
  1b:   90                      nop
  1c:   90                      nop
  1d:   90                      nop
  1e:   90                      nop
  1f:   90                      nop

Update2

バイナリ エディタを使用して、te1.o (gcc で生成) のマシン コードを変更lea 0(%rip),%rcxlea -0x4(%rip),%rcx、tcc に変更してリンクすると、結果の exe は正常に動作します。より正確には、私はそうしました

c:\tcc> gcc -c te1.c
c:\tcc> objcopy -O  elf64-x86-64 te1.o 
c:\tcc> use a binary editor to the change the bytes from (48 8d 0d 00 00 00 00) to (48 8d 0d fc ff ff ff)
c:\tcc> tcc te2.c te1.o
c:\tcc> te2
1234567in dummy!!!

アップデート 3

要求に応じて、ここに出力がありますobjdump -r te1.o

C:\temp>gcc -c te1.c

C:\temp>objdump -r te1.o

te1.o:     file format pe-x86-64

RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE
000000000000000b R_X86_64_PC32     .rdata
0000000000000010 R_X86_64_PC32     printf1


RELOCATION RECORDS FOR [.pdata]:
OFFSET           TYPE              VALUE
0000000000000000 rva32             .text
0000000000000004 rva32             .text
0000000000000008 rva32             .xdata



C:\temp>objdump -d te1.o

te1.o:     file format pe-x86-64


Disassembly of section .text:

0000000000000000 <dummy>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 20             sub    $0x20,%rsp
   8:   48 8d 0d 00 00 00 00    lea    0x0(%rip),%rcx        # f <dummy+0xf>
   f:   e8 00 00 00 00          callq  14 <dummy+0x14>
  14:   90                      nop
  15:   48 83 c4 20             add    $0x20,%rsp
  19:   5d                      pop    %rbp
  1a:   c3                      retq
  1b:   90                      nop
  1c:   90                      nop
  1d:   90                      nop
  1e:   90                      nop
  1f:   90                      nop
4

2 に答える 2

7

tcc呼び出し規約とは何の関係もありません。フォーマットのさまざまなリンカー規則に関係していelf64-x86-64 and pe-x86-64ます。

PE を使用すると、リンカーは暗黙的に 4 を減算して、最終的なオフセットを計算します。

ELF では、これは行われません。このため、PE の正しい初期値は 0、ELF の正しい初期値は -4 です。

残念ながら、objcopyこれは変換されません -> のバグobjcopy

于 2016-07-17T09:37:37.353 に答える
5

追加

extern void printf1(char *p);

te1.c ファイルに

または: プロトタイプがなく、ポインタが 64 ビット長であるため、コンパイラは引数を 32 ビット整数と見なします。

編集:これはまだ機能していません。関数が返されないことがわかりました(printf1を2回呼び出しても何も起こらないためです!)。最初の 4 バイトがリターン アドレスなどとして消費されているようです。gcc 32 ビット モードでは問題なく動作します。私には呼び出し規約の問題のように思えますが、それでも理解できません。別の手がかり:サイド (gcc、tcc stdlib バインディングを使用)printfから呼び出すと、segv でクラッシュします。te1.c

実行可能ファイルを逆アセンブルしました。最初の部分は tcc 側からの繰り返し呼び出しです

  40104f:       48 8d 05 b3 0f 00 00    lea    0xfb3(%rip),%rax        # 0x402009
  401056:       48 89 45 f8             mov    %rax,-0x8(%rbp)
  40105a:       48 8b 4d f8             mov    -0x8(%rbp),%rcx
  40105e:       e8 9d ff ff ff          callq  0x401000
  401063:       48 8b 4d f8             mov    -0x8(%rbp),%rcx
  401067:       e8 94 ff ff ff          callq  0x401000
  40106c:       48 8b 4d f8             mov    -0x8(%rbp),%rcx
  401070:       e8 8b ff ff ff          callq  0x401000
  401075:       48 8b 4d f8             mov    -0x8(%rbp),%rcx
  401079:       e8 82 ff ff ff          callq  0x401000
  40107e:       e8 0d 00 00 00          callq  0x401090
  401083:       b8 00 00 00 00          mov    $0x0,%eax
  401088:       e9 00 00 00 00          jmpq   0x40108d
  40108d:       c9                      leaveq
  40108e:       c3                      retq

2 番目の部分は、同じ関数を繰り返し (6 回) 呼び出します。ご覧のとおり、アドレスが異なります(データのように4バイトシフトされています)!!! 最初の4つの指示は次のとおりであるため、一度だけ機能します。

 401000:       55                      push   %rbp
 401001:       48 89 e5                mov    %rsp,%rbp

それらがスキップされると、スタックが破壊されます!!

  40109f:       48 89 45 f8             mov    %rax,-0x8(%rbp)
  4010a3:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4010a7:       48 89 c1                mov    %rax,%rcx
  4010aa:       e8 55 ff ff ff          callq  0x401004
  4010af:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4010b3:       48 89 c1                mov    %rax,%rcx
  4010b6:       e8 49 ff ff ff          callq  0x401004
  4010bb:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4010bf:       48 89 c1                mov    %rax,%rcx
  4010c2:       e8 3d ff ff ff          callq  0x401004
  4010c7:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4010cb:       48 89 c1                mov    %rax,%rcx
  4010ce:       e8 31 ff ff ff          callq  0x401004
  4010d3:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4010d7:       48 89 c1                mov    %rax,%rcx
  4010da:       e8 25 ff ff ff          callq  0x401004
  4010df:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4010e3:       48 89 c1                mov    %rax,%rcx
  4010e6:       e8 19 ff ff ff          callq  0x401004
  4010eb:       90                      nop
于 2016-07-12T19:15:13.047 に答える