#include <stdio.h>
#include <string.h>
#define CONFDIR "/opt/hp9300/pov64_IworkspaceIdocpv989Ieightonews-r7.6-dev_tests-000001"
#define NEW_CONFDIR "/etc" CONFDIR
// test.c
int foo() {
char confdir[127+1]; // plz ignore the buffer checks
strcpy(confdir, NEW_CONFDIR);
// strncpy(confdir, NEW_CONFDIR, sizeof(confdir));
return 0;
}
// output when strcpy
// objdump -s test.so
// no .rodata section, only .text
Contents of section .text:
1040 488d3dc1 2f000048 8d05ba2f 00004839 H.=./..H.../..H9
1050 f8741548 8b056e2f 00004885 c07409ff .t.H..n/..H..t..
1060 e00f1f80 00000000 c30f1f80 00000000 ................
1070 488d3d91 2f000048 8d358a2f 00004829 H.=./..H.5./..H)
1080 fe4889f0 48c1ee3f 48c1f803 4801c648 .H..H..?H...H..H
1090 d1fe7414 488b053d 2f000048 85c07408 ..t.H..=/..H..t.
10a0 ffe0660f 1f440000 c30f1f80 00000000 ..f..D..........
10b0 f30f1efa 803d4d2f 00000075 2b554883 .....=M/...u+UH.
10c0 3d1a2f00 00004889 e5740c48 8d3d2e2d =./...H..t.H.=.-
10d0 0000e859 ffffffe8 64ffffff c605252f ...Y....d.....%/
10e0 0000015d c30f1f00 c30f1f80 00000000 ...]............
10f0 f30f1efa e977ffff ff554889 e54883ec .....w...UH..H..
1100 08488d45 8048ba2f 6574632f 6f707448 .H.E.H./etc/optH
1110 b92f6870 39333030 2f488910 48894808 ./hp9300/H..H.H.
1120 48be706f 7636345f 497748bf 6f726b73 H.pov64_IwH.orks
1130 70616365 48897010 48897818 48ba4964 paceH.p.H.x.H.Id
1140 6f637076 393848b9 39496569 6768746f ocpv98H.9Ieighto
1150 48895020 48894828 48be6e65 77732d72 H.P H.H(H.news-r
1160 372e48bf 362d6465 765f7465 48897030 7.H.6-dev_teH.p0
1170 48897838 48ba6576 5f746573 747348b9 H.x8H.ev_testsH.
1180 2d303030 30303100 4889503b 48894843 -000001.H.P;H.HC
1190 b8000000 00c9c3 .......
// output when strncpy
// objdump -s test.so
// readelf -p ".rodata" test.so
Contents of section .rodata:
2000 2f657463 2f6f7074 2f687039 3330302f /etc/opt/hp9300/
2010 706f7636 345f4977 6f726b73 70616365 pov64_Iworkspace
2020 49646f63 70763938 39496569 6768746f Idocpv989Ieighto
2030 6e657773 2d72372e 362d6465 765f7465 news-r7.6-dev_te
2040 7374732d 30303030 303100 sts-000001.
// output when strncpy
// readelf -p ".rodata" test.so
String dump of section '.rodata':
[ 0] /etc/opt/hp9300/pov64_IworkspaceIdocpv989Ieightonews-r7.6-dev_tests-000001
当我使用 gcc -o test.so -fPIC -shared -g 在 Linux 上编译它并使用 objdump 或字符串检查共享 lib test.so 的字符串常量时,我看到 NEW_CONFDIR 的子字符串,被截断为多个16 字节。
但是,如果我更改代码以使用 strncpy (如上面的代码所示)并使用同一组标志再次编译,它会显示完整的字符串,没有截断。
这是预期的吗?编译器是否对字符串常量进行了任何优化? 或者在这种情况下使用 strcpy 和 strncpy 存在问题及其对共享对象文件的rodata 的影响?
我只关心编译阶段和共享对象的生成,而不关心代码的实际执行。 该工具在共享库的rodata中搜索NEW_CONFDIR。
我重现了这种行为:获取OP的源代码,使用
gcc -o test.so -fPIC -shared -g
进行编译,运行strings test.so
:
_ITM_deregisterTMCloneTable
_ITM_registerTMCloneTable
__cxa_finalize
u+UH
/etc/optH
/hp9300/H
pov64_IwH
orkspaceH
Idocpv98H
9IeightoH
news-r7.H
6-dev_teH
ev_testsH
-000001
;*3$"
GCC: (Debian 13.2.0-5) 13.2.0
...
那么让我们弄清楚发生了什么。
objdump -d test.so
显示:
00000000000010f9 <foo>:
10f9: 55 push %rbp
10fa: 48 89 e5 mov %rsp,%rbp
10fd: 48 83 ec 08 sub $0x8,%rsp
; RAX = &configdir[0]
1101: 48 8d 45 80 lea -0x80(%rbp),%rax
1105: 48 ba 2f 65 74 63 2f movabs $0x74706f2f6374652f,%rdx
110c: 6f 70 74
110f: 48 b9 2f 68 70 39 33 movabs $0x2f3030333970682f,%rcx
1116: 30 30 2f
; copy 8 bytes "/etc/opt" to &confdir[0]
1119: 48 89 10 mov %rdx,(%rax)
; copy "/hp9300/" to &confdir[8]
111c: 48 89 48 08 mov %rcx,0x8(%rax)
; ... etc.
1120: 48 be 70 6f 76 36 34 movabs $0x77495f3436766f70,%rsi
1127: 5f 49 77
112a: 48 bf 6f 72 6b 73 70 movabs $0x65636170736b726f,%rdi
1131: 61 63 65
1134: 48 89 70 10 mov %rsi,0x10(%rax)
1138: 48 89 78 18 mov %rdi,0x18(%rax)
113c: 48 ba 49 64 6f 63 70 movabs $0x38397670636f6449,%rdx
1143: 76 39 38
1146: 48 b9 39 49 65 69 67 movabs $0x6f74686769654939,%rcx
114d: 68 74 6f
1150: 48 89 50 20 mov %rdx,0x20(%rax)
1154: 48 89 48 28 mov %rcx,0x28(%rax)
1158: 48 be 6e 65 77 73 2d movabs $0x2e37722d7377656e,%rsi
115f: 72 37 2e
1162: 48 bf 36 2d 64 65 76 movabs $0x65745f7665642d36,%rdi
1169: 5f 74 65
116c: 48 89 70 30 mov %rsi,0x30(%rax)
1170: 48 89 78 38 mov %rdi,0x38(%rax)
1174: 48 ba 65 76 5f 74 65 movabs $0x73747365745f7665,%rdx
117b: 73 74 73
117e: 48 b9 2d 30 30 30 30 movabs $0x3130303030302d,%rcx
1185: 30 31 00
1188: 48 89 50 3b mov %rdx,0x3b(%rax)
118c: 48 89 48 43 mov %rcx,0x43(%rax)
1190: b8 00 00 00 00 mov $0x0,%eax
1195: c9 leave
1196: c3 ret
那么
H
从何而来?
查看从 0x1122 开始的“字符串”常量后面的字节。
MOVABS
指令代码是0x48
0xbf
,而0x48
恰好“拼写”H
,这就是为什么那里有一个H
。
有关 AWAVAUATUSH
和类似字符串的相关
answer。
那么当您使用
strncpy
时会发生什么?
编译器决定调用外部版本而不是内联
strcpy
:
objdump -d test.so
...
0000000000001109 <foo>:
1109: 55 push %rbp
110a: 48 89 e5 mov %rsp,%rbp
110d: 48 83 c4 80 add $0xffffffffffffff80,%rsp
1111: 48 8d 45 80 lea -0x80(%rbp),%rax
1115: ba 80 00 00 00 mov $0x80,%edx
111a: 48 8d 0d df 0e 00 00 lea 0xedf(%rip),%rcx # 2000 <_fini+0xecc>
1121: 48 89 ce mov %rcx,%rsi
1124: 48 89 c7 mov %rax,%rdi
1127: e8 04 ff ff ff call 1030 <strncpy@plt>
112c: b8 00 00 00 00 mov $0x0,%eax
1131: c9 leave
1132: c3 ret