请原谅我这个问题。我寻找类似问题的答案,但我仍然对我的问题感到困惑。所以无论如何我会拍这个问题。 我正在使用名为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()
并将返回的值赋给一个类型为enum(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-Qt Media hub中使用libexif(实际上libexif与Qt Media hub一起使用)
EDIT2:其他一些观察: 我正在观察奇怪的事情。我已将print语句放在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
答案 0 :(得分:4)
MIPS程序集显示的一件事(虽然我不是MIPS程序集的专家,所以我有一个不错的机会我错过了什么或其他错误)是exif_get_short()
函数只是一个别名exif_get_sshort()
功能。 exif_get_short()
所做的只是跳转到exif_get_sshort()
函数的地址。
exif_get_sshort()
函数符号扩展了它返回到用于返回的完整32位寄存器的16位值。这没有什么不对 - 它实际上可能是MIPS ABI指定的(我不确定)。
但是,由于exif_get_short()
函数只是跳转到exif_get_sshort()
函数,因此没有机会清除寄存器的高16位。
因此,当从缓冲区返回16位值0x8769时(无论是来自exif_get_sshort()
还是exif_get_short()
),用于返回函数结果的$2
寄存器包含{{1} },可以有以下解释:
0xffff8769
: - 30871 作为32位`unsigned int:4294936425
作为16位签名signed int
: - 30871
int16_t
:34665 如果编译器应该确保uint16_t
返回寄存器对$2
返回类型的前16位设置为零,那么它在为{发出的代码中有一个错误{1}} - 而不是跳转到uint16_t
,它应该在返回之前调用exif_get_short()
并清除exif_get_sshort()
的上半部分。
根据您所看到的行为的描述,看起来调用exif_get_sshort()
的代码期望用于返回值的$2
寄存器将清除高16位以便整个32位寄存器可以原样用于16位exif_get_short()
值。
我不确定MIPS ABI指定了什么(但我猜它指定$2
寄存器的高16位应该由uint16_t
清除,但似乎是$2
在返回之前不确保exif_get_short()
完全正确的代码生成错误,或者是exif_get_short()
的调用者假定{{1}的完整32位的错误仅当16位时才有效。
答案 1 :(得分:0)
这在很多层面都被打破了,我不知道从哪里开始。看看这里做了什么:
int16_t
中的签名 exif_get_sshort
。uint16_t
中的未签名 exif_get_short
。enum
。我认为这是一个奇迹,它可以起作用。
首先,从字符到int
的赋值是使用值完成的,而不是使用表示:
int16_t
当结果实际为负时,已经将你引入了未定义行为的陷阱。另外,它仅在实现的signed int表示与文件格式中使用的表示相同时才起作用(我想是两个补码)。它会因一个补码和符号幅度而失败。因此,请检查MIPS实现的情况。
干净的方式将是另一种方式:将两个字符从缓冲区分配给return ((buf[0] << 8) | buf[1]);
,这将是明确定义的操作,并使用它来返回uint16_t
。如有必要,您可以关注不同表示的值的进一步更正。
此外,在这里:
int16_t
0是返回值的一个非常糟糕的选择,因为它是一个有效的枚举常量:
if (!buf) return 0;
如果预期这是无效的默认值,则应返回常量,而不是幻数。虽然这似乎是返回EXIF_TAG_GPS_VERSION_ID = 0x0000,
的通用函数,但是这里应该使用其他一些错误机制。
对于您的具体问题,请遵循MIPS实施中已签名和未签名的转换流程,包括已完成的默认促销,并检查所有中间值以找到中断点。你的MIPS使用32位整数,而不是16位,对吗?检查INT_MAX和UINT_MAX。