如果之前有人提出过这个问题,请原谅。我寻找类似问题的答案,但我仍然对我的问题感到困惑。所以无论如何我都会回答这个问题。 我正在使用一个名为 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) 类型的变量。
在错误情况下,本应返回无符号值 (34687) 的 exif_get_short()
返回负数 (-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 桌面上针对错误文件运行这段代码时,我没有发现任何问题,而且使用原始库代码也能正常工作。这让我相信 UINT16_MAX 和 INT16_MAX 宏在我的桌面和 MIPS 板上可能具有不同的值。但不幸的是,事实并非如此。两者都在电路板和桌面上打印相同的值。如果这段代码失败,它在我的桌面上也应该失败。
我在这里错过了什么?任何提示将不胜感激。
编辑: 调用 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 hub(实际上 libexif 与 Qt Media hub 一起提供)
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: 返回值 34665 34665
然后我还在调用 exif_get_short() 的代码中插入了打印语句
....
tag = exif_get_short (d + offset + 12 * i, data->priv->order);
printf("TAG %d %u\n",tag,tag);
我看到以下 o/p: 标签-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
最佳答案
MIPS 程序集显示的一件事(虽然我不是 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
返回前。
从您所看到的行为描述来看,调用 exif_get_short()
的代码似乎期望用于返回值的 $2
寄存器将具有清除高 16 位,以便整个 32 位寄存器可以按原样用于 16 位 uint16_t
值。
我不确定 MIPS ABI 指定了什么(但我猜它指定 $2
寄存器的高 16 位应该由 exif_get_short()
),但似乎存在 exif_get_short()
无法确保 $2
在返回之前完全正确的代码生成错误,或者调用者的错误exif_get_short()
假定 $2
的全部 32 位有效,而只有 16 位有效。
关于c - 整数溢出不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12032122/