0

(免責事項:これは巨大なテキストの壁だと思いますが、私は物事を本質にまで煮詰めるために最善を尽くしました。libtiffに精通している場合、これはそれほど複雑な質問ではありません)

すでにlibtiffメーリングリストでこの質問をしましたが、図書館で働いたことのある人がいれば、ここでもチャンスがあるのではないかと思いました。

http://libtiff.maptools.org/addingtags.htmlのドキュメントを使用して、独自の組み込みタグをライブラリに追加しています。

そこで、tif_dirinfo.cの先頭に定義されているTIFFFieldInfo配列に次のようにエントリを追加しました。

{ TIFFTAG_CUSTOM_XXX, 4, 4, TIFF_SLONG, FIELD_XXX, 1, 0, "XXX" }, 

次に、 :TIFFDirectoryで定義された構造にフィールドを追加しました。tif_dir.h

typedef struct {
    /* ... */
    int32 td_xxx[4]; 
} TIFFDirectory;

今、私は先に進んで、指示通りに修正_TIFFVSetFieldしました。_TIFFVGetFieldこれは私が問題にぶつかったところです。

ライブラリにすでに存在するパターンを模倣する際に(TIFFTAG_YCBCRSUBSAMPLING私が行っていることに類似しているの実装を参照)、次のコードを次のように追加しました_TIFFVGetField

/* existing, standard tag for reference */
case TIFFTAG_YCBCRSUBSAMPLING:
        *va_arg(ap, uint16*) = td->td_ycbcrsubsampling[0];
        *va_arg(ap, uint16*) = td->td_ycbcrsubsampling[1];
        break;
/* my new tag */
case TIFFTAG_CUSTOM_XXX: 
        *va_arg(ap, int32*) = td->td_xxx[0]; 
        *va_arg(ap, int32*) = td->td_xxx[1]; 
        *va_arg(ap, int32*) = td->td_xxx[2]; 
        *va_arg(ap, int32*) = td->td_xxx[3]; 
        break; 

私が知る限り、これは完全に間違っています。ここでの目的は、intの配列に基づいて変数引数リストに入力を入力することです。1つの節約の恩恵は、で提供される引数va_listが常に型int32であり、YcBrコードが2つint16の'を使用することです。したがって、それは機能しますが、その実装をコピーすることはできません。

_TIFFVGetField最終的にから呼び出さTIFFWriteNormalTagtif_dirwrite.cます。関連するコードは次のとおりです。

case TIFF_LONG: 
    case TIFF_SLONG: 
    case TIFF_IFD: 
        if (fip->field_passcount) { 
            uint32* lp; 
            if (wc == (uint16) TIFF_VARIABLE2) { 
                TIFFGetField(tif, fip->field_tag, &wc2, &lp); 
                TDIRSetEntryCount(tif,dir, wc2); 
            } else {    /* Assume TIFF_VARIABLE */ 
                TIFFGetField(tif, fip->field_tag, &wc, &lp); 
                TDIRSetEntryCount(tif,dir, wc); 
            } 
            if (!TIFFWriteLongArray(tif, dir, lp)) 
                return 0; 
            } else { 
                if (wc == 1) { 
                    uint32 wp; 
                    /* XXX handle LONG->SHORT conversion */ 
                    TIFFGetField(tif, fip->field_tag, &wp); 
                    TDIRSetEntryOff(tif,dir, wp); 
                } else { 
                /* ---------------------------------------------------- */ 
                /* this is the code that is called in my scenario       */
                /* ---------------------------------------------------- */ 
                    uint32* lp; 
                    TIFFGetField(tif, fip->field_tag, &lp); 
                    if (!TIFFWriteLongArray(tif, dir, lp)) 
                        return 0; 
                } 
            } 
            break; 

したがって、初期化されていないポインタlpが宣言され、そのアドレスがに渡されTIFFGetFieldます。これにより、va_list(lp唯一の引数として)が設定され、TIFFVGetFieldが呼び出されます。これは_TIFFVGetField、指定さva_listれたポインタと初期化されていないポインタへのポインタを使用して呼び出します。

ここには2つの問題があります。

まず、これはライブラリがデータを抽出する方法です(私のコードですが、ここでも、すでに存在するパターンに従います)

*va_arg(ap, int32*) = td->td_xxx[0]; 

これは正しくないようです。元のポインタをintの値に設定しています。おそらく、私がフォローしている例(TIFFTAG_YCBCRSUBSAMPLING)では、これらの整数は実際にはアドレスであると考えました。大丈夫ですが、それでも別の問題があります。

ライブラリはva_args Ntimesを呼び出します。ここNで、は配列内の要素の数です。私が見るところ、可変引数リストには単一の引数(ポインターのアドレス)しか含まれていません。これは、標準による未定義の動作です(最初の重要なビット):

実際の次の引数がない場合、またはタイプが実際の次の引数のタイプと互換性がない場合(デフォルトの引数の昇格に従って昇格)、動作は定義されていません。

正しいバージョンは

*va_arg(ap, int32**) = td_xxx; 

これにより、以前に初期化されていないポインタが配列に設定されます。これは有効です。コピーではなくデータ自体を指すのは好きではありませんが、何でも構いません。少なくともクラッシュせず、正しい結果が得られます。

ここでの私の懸念は、微妙な何かが欠けていることです。このソフトウェアは古く、多くの人々によって使用されています。そのため、これをバグと呼ぶと、コンパイラのクラッシュを非難するように感じますが、これはほとんどの場合間違っています。

ただし、これが正しい方法、具体的には、ライブラリがva_arg複数回呼び出されたときに返されるものに書き込む方法を推測することはできません。

どんな助けでも大歓迎です。前もって感謝します。

4

1 に答える 1

0

したがって、ここでの答えは、最終的にlibtiffがこれをUBに依存しているということでした。技術的にはUBですが、次のva_argようなことを行わなかった実装は見つかりませんでした。

( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

tしたがって、 (ここにあるように)元の引数のサイズよりも小さい限り、va_arg安全に複数回呼び出すことができます。

私は自分のデータにつながるポインタに引数を設定するだけで終わり、それは機能します。ヘッダーデータ自体に直接アクセスするのは好きではありませんが、ライブラリを大幅に変更することなく、それが唯一の選択肢でした。

于 2012-03-25T18:42:12.643 に答える