8

この質問が以前に提起された場合は、ご容赦ください。同様の質問への回答を探しましたが、まだ自分の問題に戸惑っています。だから私はとにかく質問を撃ちます。画像データにはlibexifという C ライブラリを使用しています。Linux デスクトップと MIPS ボードの両方でアプリケーション (このライブラリを使用) を実行しています。特定の画像ファイルの作成時間を取得しようとすると、エラーまたは無効な値が返されました。さらにデバッグすると、この特定の画像ファイルについて、期待どおりにタグ (EXIF_TAG_DATE_TIME) を取得していないことがわかりました。

このライブラリには、いくつかのユーティリティ関数があります。ほとんどの関数は次のように構成されています

int16_t 
exif_get_sshort (const unsigned char *buf, ExifByteOrder order)
{
    if (!buf) return 0;
        switch (order) {
        case EXIF_BYTE_ORDER_MOTOROLA:
                return ((buf[0] << 8) | buf[1]);
        case EXIF_BYTE_ORDER_INTEL:
                return ((buf[1] << 8) | buf[0]);
        }

    /* Won't be reached */
    return (0);
}

uint16_t
exif_get_short (const unsigned char *buf, ExifByteOrder order)
{
    return (exif_get_sshort (buf, order) & 0xffff);
}

ライブラリは、生データ内のタグの存在を調査しようとすると、呼び出しexif_get_short()て、返された値を列挙型 (int) の変数に割り当てます。

exif_get_short()符号なし値 (34687) を返すはずのエラーの場合、負の数 (-30871) が返され、画像データからのタグ抽出全体が台無しになります。

34687 は int16_t の表現可能な最大値の範囲外です。したがって、オーバーフローにつながります。コードにこのわずかな変更を加えると、すべて正常に動作するように見えます

uint16_t
exif_get_short (const unsigned char *buf, ExifByteOrder order)
{
    int temp = (exif_get_sshort (buf, order) & 0xffff);
        return temp;
}

しかし、これは非常に安定したライブラリであり、かなり長い間使用されているため、ここに何かが欠けているのではないかと思いました。さらに、これはコードが他のユーティリティ関数に対しても構造化される一般的な方法です。例:exif_get_long()呼び出しますexif_get_slong()。次に、すべてのユーティリティ関数を変更する必要があります。

私を混乱させているのは、Linuxデスクトップでエラーファイルに対してこのコードを実行すると、問題がなく、元のライブラリコードで問題なく動作することです。これにより、私のデスクトップと MIPS ボードでは、おそらく UINT16_MAX マクロと INT16_MAX マクロの値が異なるのではないかと思いました。しかし、残念ながらそうではありません。どちらも、ボードとデスクトップに同じ値を出力します。このコードが失敗した場合、私のデスクトップでも失敗するはずです。

ここで何が欠けていますか?どんなヒントでも大歓迎です。

編集: exif_get_short() を呼び出すコードは次のようになります。

ExifTag tag;
...
tag = exif_get_short (d + offset + 12 * i, data->priv->order);
switch (tag) {
...
...

タイプ ExifTag は次のとおりです。

typedef enum {
    EXIF_TAG_GPS_VERSION_ID             = 0x0000,
EXIF_TAG_INTEROPERABILITY_INDEX     = 0x0001,
    ...
    ...
    }ExifTag ;

使用されているクロス コンパイラは mipsisa32r2el-timesys-linux-gnu-gcc です。

CFLAGS        = -pipe -mips32r2 -mtune=74kc -mdspr2 -Werror -O3 -Wall -W -D_REENTRANT -fPIC $(DEFINES)

Qt内でlibexifを使用しています-Qt Mediaハブ(実際にはlibexifはQt Mediaハブに付属しています)

EDIT2:いくつかの追加の観察:私は何か奇妙なことを観察しています。印刷ステートメントを exif_get_short() に入れました。帰国直前

printf("return_value %d\n %u\n",exif_get_sshort (buf, order) & 0xffff, exif_get_sshort (buf, order) & 0xffff);
return (exif_get_sshort (buf, order) & 0xffff);

次の o/p が表示されます: return_value 34665 34665

次に、exif_get_short() を呼び出すコードに print ステートメントも挿入しました。

....
tag = exif_get_short (d + offset + 12 * i, data->priv->order);
printf("TAG %d %u\n",tag,tag);

次の o/p が表示されます: TAG -30871 4294936425

EDIT3 : MIPS ボードで撮影した exif_get_short() および exif_get_sshort() のアセンブリ コードの投稿

        .file   1 "exif-utils.c"
    .section .mdebug.abi32
    .previous
    .gnu_attribute 4, 1
    .abicalls
    .text
    .align  2
    .globl  exif_get_sshort
    .ent    exif_get_sshort
    .type   exif_get_sshort, @function
exif_get_sshort:
    .set    nomips16
    .frame  $sp,0,$31       # vars= 0, regs= 0/0, args= 0, gp= 0
    .mask   0x00000000,0
    .fmask  0x00000000,0
    .set    noreorder
    .set    nomacro

    beq $4,$0,$L2
    nop

    beq $5,$0,$L3
    nop

    li  $2,1            # 0x1
    beq $5,$2,$L8
    nop

$L2:

    j   $31
    move    $2,$0

$L3:

    lbu $2,0($4)
    lbu $3,1($4)
    sll $2,$2,8
    or  $2,$2,$3
    j   $31
    seh $2,$2

$L8:

    lbu $2,1($4)
    lbu $3,0($4)
    sll $2,$2,8
    or  $2,$2,$3
    j   $31
    seh $2,$2

    .set    macro
    .set    reorder
    .end    exif_get_sshort
    .align  2
    .globl  exif_get_short
    .ent    exif_get_short
    .type   exif_get_short, @function

exif_get_short:

    .set    nomips16
    .frame  $sp,0,$31       # vars= 0, regs= 0/0, args= 0, gp= 0
    .mask   0x00000000,0
    .fmask  0x00000000,0
    .set    noreorder
    .cpload $25
    .set    nomacro

    lw  $25,%call16(exif_get_sshort)($28)
    jr  $25
    nop

    .set    macro
    .set    reorder
    .end    exif_get_short

完全を期すために、私の Linux マシンから取得した ASM コード

    .file   "exif-utils.c"
    .text
    .p2align 4,,15
    .globl  exif_get_sshort
    .type   exif_get_sshort, @function

exif_get_sshort:

.LFB1:

        .cfi_startproc
    xorl    %eax, %eax
    testq   %rdi, %rdi
    je  .L2
    testl   %esi, %esi
    jne .L8
    movzbl  (%rdi), %edx
    movzbl  1(%rdi), %eax
    sall    $8, %edx
    orl %edx, %eax
    ret
    .p2align 4,,10
    .p2align 3

.L8:
    cmpl    $1, %esi
    jne .L2
    movzbl  1(%rdi), %edx
    movzbl  (%rdi), %eax
    sall    $8, %edx
    orl %edx, %eax

.L2:
    rep
    ret
    .cfi_endproc

.LFE1:
    .size   exif_get_sshort, .-exif_get_sshort
    .p2align 4,,15
    .globl  exif_get_short
    .type   exif_get_short, @function

exif_get_short:

.LFB2:
    .cfi_startproc
    jmp exif_get_sshort@PLT
    .cfi_endproc
.LFE2:
    .size   exif_get_short, .-exif_get_short

EDIT4:私の最後の更新を願っています:-)コンパイラオプションが-O1に設定されたASMコード

exif_get_short:

.set    nomips16
.frame  $sp,32,$31      # vars= 0, regs= 1/0, args= 16, gp= 8
.mask   0x80000000,-4
.fmask  0x00000000,0
.set    noreorder
.cpload $25
.set    nomacro

addiu   $sp,$sp,-32
sw  $31,28($sp)
.cprestore  16
lw  $25,%call16(exif_get_sshort)($28)
jalr    $25
nop

lw  $28,16($sp)
andi    $2,$2,0xffff
lw  $31,28($sp)
j   $31
addiu   $sp,$sp,32

.set    macro
.set    reorder
.end    exif_get_short
4

2 に答える 2

4

MIPS アセンブリが示すことの 1 つは (私は MIPS アセンブリの専門家ではありませんが、何かを見落としているか間違っている可能性が十分にあります)、exif_get_short()関数は関数のエイリアスに過ぎないということexif_get_sshort()です。関数のexif_get_short()アドレスにジャンプするだけexif_get_sshort()です。

関数符号は、exif_get_sshort()返される 16 ビット値を、戻り値に使用される完全な 32 ビット レジスタに拡張します。それは何も悪いことではありません - それはおそらく MIPS ABI が指定するものです (よくわかりません)。

ただし、exif_get_short()関数は関数にジャンプするだけexif_get_sshort()なので、レジスタの上位 16 ビットをクリアする機会はありません。

そのため、16 ビット値 0x8769 がバッファーから (exif_get_sshort()またはからexif_get_short())$2返される場合、関数の結果を返すために使用されるレジスタに0xffff8769は、次の解釈が可能な が含まれます。

  • 32 ビットとしてsigned int: -30871
  • 32 ビットの `unsigned int: 4294936425 として

  • 16 ビットの符号付きint16_t: -30871

  • 16 ビット符号なしuint16_t: 34665

コンパイラーが$2、戻り型の戻りレジスターの上位 16 ビットがゼロに設定されていることを保証することになっている場合uint16_t、それが出力しているコードにバグがありますexif_get_short()- にジャンプする代わりに、上半分exif_get_sshort()を呼び出しexif_get_sshort()てクリアする必要があります$2戻る前の。

あなたが見ている動作の説明から、コード呼び出しでは、戻り値に使用されるレジスタの上位 16 ビットがクリアされ、32 ビット レジスタ全体をそのまま 16 に使用できることがexif_get_short()期待されているように見えます。 $2-ビットuint16_t値。

MIPS ABI が何を指定しているのかはわかりません (ただし、$2レジスタの上位 16 ビットは によってクリアされる必要があると指定されていると思いますexif_get_short())。または、 の呼び出し元が の完全な 32ビットが 16 ビットのみ有効であると想定するバグ。exif_get_short()$2exif_get_short()$2

于 2012-08-21T08:33:45.780 に答える
0

これは非常に多くのレベルで壊れているため、どこから始めればよいかわかりません。ここで行われていることを見てください:

  • unsigned charsはバッファから読み取られます。
  • それらは、サイン int16_tインしている に割り当てられexif_get_sshortます。
  • これは、サイン uint16_tインしていない に割り当てられexif_get_shortます。
  • これは最終的にsignedenumタイプの に割り当てられます。 int

それがまったく機能するのは奇跡だと思います。

まず、chars から への割り当てはint16_t、表現ではなく値で行われます。

return ((buf[0] << 8) | buf[1]);

結果が実際に負の場合、これはすでに未定義の動作の穴に陥っています。さらに、実装の符号付き int 表現がファイル形式で使用されているものと同じ場合にのみ機能します (2 の補数だと思います)。1 の補数と符号の大きさでは失敗します。したがって、MIPS 実装の場合はどうなるかを確認してください。

クリーンな方法はその逆です。バッファから に 2 つの文字を割り当てます。uint16_tこれは明確に定義された操作であり、これを使用して を返しますint16_t。その後、必要に応じて、さまざまな表現の値をさらに修正することができます。

さらに、ここで:

if (!buf) return 0;

0 は有効な列挙型定数であるため、戻り値として非常に悪い選択です。

EXIF_TAG_GPS_VERSION_ID             = 0x0000,

これが無効のデフォルトであると予想される場合は、マジック ナンバーではなく、定数を返す必要があります。これは を返す一般的な関数のように見えますが、int16_tここでは他のエラーメカニズムを使用する必要があります。

特定の質問については、MIPS 実装での符号付きと符号なしの間の変換の流れをたどり、デフォルトの昇格を含め、すべての中間値を調べて、それが壊れるポイントを見つけてください。あなたの MIPS は 16 ビットではなく 32 ビット整数を使用していますよね? INT_MAX と UINT_MAX を確認してください。

于 2012-08-20T07:30:34.010 に答える