- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我想知道是否有一种方法可以制作“真正的位置独立代码”。
想象一下这种情况(这里的实现真的不重要。):您有一个名为“a.c”的文件,其中包含 c
代码的两个简单函数 - functin a
和函数 b
,当 fnction a
调用函数 `b'..
当编译这个简单的情况时:gcc -c -o a.out a.c -fPIC
然后观察 text
部分,我会看到函数 a
,在程序集中,在它调用函数 b
的地方 - 一个保持位置零。众所周知,这归零,将根据重定位表中的值在运行时简单地替换。
好吧,这是我的问题 - 我发现它是独立性的不必要部分。原因是,因为在运行之前,我可以确定 a
和 b
之间的距离,因此我可以重写表示 b 的包含零的位置
地址,与实际函数的调用相对。我发现了这种手动更改字节的方法 - 当我们谈论大型程序时它非常长。
那么,有没有什么优雅的方法可以通过 gcc
/objdump
或其他东西的标志来完成我手动执行的相同操作?
最佳答案
考虑这个独立的 hello.c,用于 Linux 上的 x86-64/AMD64 架构:
/* Freestanding Hello World example in Linux on x86_64/x86.
* Compile using
* gcc -Wall -O2 -fPIC -pie -march=x86-64 -mtune=generic -m64 -ffreestanding -nostdlib -nostartfiles hello.c -o hello
*/
#define STDOUT_FILENO 1
#define EXIT_SUCCESS 0
#ifndef __x86_64__
#error This program only works on x86_64 architecture!
#endif
#define SYS_write 1
#define SYS_exit 60
#define SYSCALL1_NORET(nr, arg1) \
__asm__ volatile ( "syscall\n\t" \
: \
: "a" (nr), "D" (arg1) \
: "rcx", "r11" )
#define SYSCALL3(retval, nr, arg1, arg2, arg3) \
__asm__ volatile ( "syscall\n\t" \
: "=a" (retval) \
: "a" (nr), "D" (arg1), "S" (arg2), "d" (arg3) \
: "rcx", "r11" )
static void my_exit(int retval)
{
SYSCALL1_NORET(SYS_exit, retval);
}
static int my_write(int fd, const void *data, int len)
{
int retval;
if (fd == -1 || !data || len < 0)
return -1;
SYSCALL3(retval, SYS_write, fd, data, len);
if (retval < 0)
return -1;
return retval;
}
static int my_strlen(const char *str)
{
int len = 0L;
if (!str)
return -1;
while (*str++)
len++;
return len;
}
static int wrout(const char *str)
{
if (str && *str)
return my_write(STDOUT_FILENO, str, my_strlen(str));
else
return 0;
}
void _start(void)
{
const char *msg = "Hello, world!\n";
wrout(msg);
my_exit(EXIT_SUCCESS);
}
请注意,它满足 OP 的场景:_start()
调用 wrout()
,后者调用 my_strlen()
和 my_write( )
。
(为什么是独立的,没有标准 C 库的所有优点?因为标准库不是用 -fPIC
和 -pie
编译的,所以它有动态链接到标准库;这些调用会进行重定位,使我的观点模糊不清。作为独立的,我们得到一个最小的、完整的、可验证的示例,它会产生清晰、明确的结果。)
编译使用
gcc -Wall -O2 -fPIC -pie -march=x86-64 -mtune=generic -m64 -ffreestanding -nostdlib -nostartfiles hello.c -o hello
并使用 ./hello
运行。它打印出“你好,世界!”应该如此。
接下来,使用 objdump -x hello
检查它:
hello: file format elf64-x86-64
hello
architecture: i386:x86-64, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
start address 0x0000000000000340
Program Header:
PHDR off 0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3
filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x
INTERP off 0x0000000000000238 vaddr 0x0000000000000238 paddr 0x0000000000000238 align 2**0
filesz 0x000000000000001c memsz 0x000000000000001c flags r--
LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**21
filesz 0x00000000000003d0 memsz 0x00000000000003d0 flags r-x
LOAD off 0x0000000000000f30 vaddr 0x0000000000200f30 paddr 0x0000000000200f30 align 2**21
filesz 0x00000000000000d0 memsz 0x00000000000000d0 flags rw-
DYNAMIC off 0x0000000000000f30 vaddr 0x0000000000200f30 paddr 0x0000000000200f30 align 2**3
filesz 0x00000000000000d0 memsz 0x00000000000000d0 flags rw-
NOTE off 0x0000000000000254 vaddr 0x0000000000000254 paddr 0x0000000000000254 align 2**2
filesz 0x0000000000000024 memsz 0x0000000000000024 flags r--
EH_FRAME off 0x0000000000000388 vaddr 0x0000000000000388 paddr 0x0000000000000388 align 2**2
filesz 0x0000000000000014 memsz 0x0000000000000014 flags r--
STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
RELRO off 0x0000000000000f30 vaddr 0x0000000000200f30 paddr 0x0000000000200f30 align 2**0
filesz 0x00000000000000d0 memsz 0x00000000000000d0 flags r--
Dynamic Section:
GNU_HASH 0x0000000000000278
STRTAB 0x0000000000000320
SYMTAB 0x00000000000002a8
STRSZ 0x0000000000000019
SYMENT 0x0000000000000018
DEBUG 0x0000000000000000
FLAGS_1 0x0000000008000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 0000000000000238 0000000000000238 00000238 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.gnu.build-id 00000024 0000000000000254 0000000000000254 00000254 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .gnu.hash 00000030 0000000000000278 0000000000000278 00000278 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .dynsym 00000078 00000000000002a8 00000000000002a8 000002a8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynstr 00000019 0000000000000320 0000000000000320 00000320 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .text 00000037 0000000000000340 0000000000000340 00000340 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
6 .rodata 0000000f 0000000000000377 0000000000000377 00000377 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .eh_frame_hdr 00000014 0000000000000388 0000000000000388 00000388 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .eh_frame 00000030 00000000000003a0 00000000000003a0 000003a0 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .dynamic 000000d0 0000000000200f30 0000000000200f30 00000f30 2**3
CONTENTS, ALLOC, LOAD, DATA
10 .comment 00000035 0000000000000000 0000000000000000 00001000 2**0
CONTENTS, READONLY
SYMBOL TABLE:
0000000000000238 l d .interp 0000000000000000 .interp
0000000000000254 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id
0000000000000278 l d .gnu.hash 0000000000000000 .gnu.hash
00000000000002a8 l d .dynsym 0000000000000000 .dynsym
0000000000000320 l d .dynstr 0000000000000000 .dynstr
0000000000000340 l d .text 0000000000000000 .text
0000000000000377 l d .rodata 0000000000000000 .rodata
0000000000000388 l d .eh_frame_hdr 0000000000000000 .eh_frame_hdr
00000000000003a0 l d .eh_frame 0000000000000000 .eh_frame
0000000000200f30 l d .dynamic 0000000000000000 .dynamic
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 l df *ABS* 0000000000000000 hello.c
0000000000000000 l df *ABS* 0000000000000000
0000000000200f30 l O .dynamic 0000000000000000 _DYNAMIC
0000000000000388 l .eh_frame_hdr 0000000000000000 __GNU_EH_FRAME_HDR
0000000000201000 l O .dynamic 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000340 g F .text 0000000000000037 _start
0000000000201000 g .dynamic 0000000000000000 __bss_start
0000000000201000 g .dynamic 0000000000000000 _edata
0000000000201000 g .dynamic 0000000000000000 _end
它只有基本的符号。您甚至可以剥离它,strip --strip-unneeded hello
,之后它就没有任何符号了。 (起始地址在ELF文件中不需要是符号。)看汇编,objdump -d hello
,
hello: file format elf64-x86-64
Disassembly of section .text:
0000000000000340 <_start>:
340: 48 8d 0d 31 00 00 00 lea 0x31(%rip),%rcx # 378 <_start+0x38>
347: 31 d2 xor %edx,%edx
349: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
350: 48 83 c1 01 add $0x1,%rcx
354: 83 c2 01 add $0x1,%edx
357: 80 79 ff 00 cmpb $0x0,-0x1(%rcx)
35b: 75 f3 jne 350 <_start+0x10>
35d: b8 01 00 00 00 mov $0x1,%eax
362: 48 8d 35 0e 00 00 00 lea 0xe(%rip),%rsi # 377 <_start+0x37>
369: 89 c7 mov %eax,%edi
36b: 0f 05 syscall
36d: 31 ff xor %edi,%edi
36f: b8 3c 00 00 00 mov $0x3c,%eax
374: 0f 05 syscall
376: c3 retq
你会看到所有寻址都是相对于 %rip
的,包括条件跳转。例如,75 f3
在下一个操作码 (0xF3 = -13) 开始之前编码 13 个字节的跳转。
如果您忽略优化 (-O2
),GCC 会尽力提供帮助并在 ELF 文件中包含 local 符号;您可以使用 strip --strip-unneeded hello
删除它们。
因此,当您在没有优化的情况下编译为目标文件时,gcc -Wall -fPIC -pie -march=x86-64 -mtune=generic -m64 -ffreestanding -nostdlib -nostartfiles -c hello.c
,并使用 objdump -x hello.o
检查生成的 hello.o
,您将看到两个局部符号(l
第二列),
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 hello.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l F .text 0000000000000016 my_exit
0000000000000016 l F .text 000000000000004e my_write
0000000000000064 l F .text 0000000000000039 my_strlen
000000000000009d l F .text 0000000000000046 wrout
0000000000000000 l d .rodata 0000000000000000 .rodata
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
00000000000000e3 g F .text 000000000000002c _start
.text
有一个搬迁记录,
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
00000000000000ee R_X86_64_PC32 .rodata-0x0000000000000004
R_X86_64_PC32
类型,是指令指针相关的32位常量。当链接到二进制文件(可执行文件或库)时,将应用这些重定位,最终二进制文件将与位置无关。
文件中唯一需要的符号是那些需要从外部访问的符号,不包括 _start
,其地址存储为 ELF 文件中的起始地址。如果编译单元外不需要函数或全局变量,则将它们标记为static
。然后,我们告诉编译器生成与位置无关的代码(-fPIC
)和与位置无关的可执行文件(-pie
)。我个人总是启用警告和优化 (-Wall -O2
),但这取决于您。
因此,OP 关于“如何做到这一点” 的问题的答案:
对不需要在当前编译单元外访问的所有函数和全局变量使用static
。
使用 -fPIC
-pie
编译对象和/或二进制文件。这应该避免运行时重定位的需要,并在支持它的所有架构上使用 %rip
相对寻址或类似寻址。
可选地,使用 strip --strip-unneeded
从二进制文件中删除不需要的符号。这不会影响重定位,但会通过删除不需要的符号信息使二进制文件更小。
关于c - 在 linux 中修改目标文件函数 xref 的优雅方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53194179/
前言: 有时候,一个数据库有多个帐号,包括数据库管理员,开发人员,运维支撑人员等,可能有很多帐号都有比较大的权限,例如DDL操作权限(创建,修改,删除存储过程,创建,修改,删除表等),账户多了,管理
这个问题已经有答案了: Condition variable deadlock (2 个回答) 已关闭 5 年前。 在研究多线程时,我编写了以下代码,但在屏幕上没有观察到输出。我在这里做错了什么?我期
复制代码 代码如下: <IfModule mod_rewrite.c> RewriteEngineOn RewriteBase/ #将www.zzvips.com跳转到www.zzv
复制代码 代码如下: <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / # 把 www.zzvips.com
复制代码 代码如下: Const T_GATEWAY = "1.1.1.1" '网关 Const T_NEWDNS1 = "2.2.2.2" 'DNS1
0. 修改索引 大文本字段支持排序 PUT http://localhost:9200/lrc_blog/_mapping //请求体 { "properties": { "title": { "t
仅 react 当状态发生变化时重新渲染 . 那么为什么我会直接看到我对真实 DOM 所做的更改呢? 我知道我正在修改真实的 DOM,但是当我根本没有改变状态时触发重新渲染的是什么。 import R
Xcode beta 5 推出 @FetchRequest对于 SwiftUI。 我有一个 View ,它有一个 @FetchRequest . NSFetchRequest是在管理器中创建的,该管理
关闭。这个问题需要更多 focused .它目前不接受答案。 想改进这个问题?更新问题,使其仅关注一个问题 editing this post . 7年前关闭。 Improve this questi
我有一个表达式[text][id]应替换为链接 text 解决方案是( id 是整数) $s = preg_replace("/\[([^\]]+)(\]*)\]\[([0-9]+)\]/","$1$
我在 repo 中有一个文件,我不想让任何人更新。 我能做什么? 最佳答案 你想要svn锁:http://www.linxit.de/svnbook/en/1.2/svn.ref.svn.c.lock
说我有项目 list 。我想导出到csv,但在此之前我想做一些计算/修改。 基本上,设置如下所示: PS C:\Files> gci Directory: C:\Files Mode
我有一个非常简单的问题 - 是否可以修改 Java API 的源代码,例如Junit,JABX ? 我知道这似乎是一个非常愚蠢的问题,但它一直困扰着我一段时间。 最佳答案 如果您可以掌握源代码,那么请
我有一个带有变量/列的小标题,其中包括不同形状的小标题列表。我想为其中一个变量中的每个(子)标题添加一个变量/列。 例如此类数据 library("tibble") aaa aaa # A tibb
我有几个菜单,可以在单击时向当前链接添加变量。这是一个例子: 1 2 3 x y z 我的问题是,如果我选择“y”2次,它会添加“&cord=y”2次。相反,我希望它替
我有两个项目:一个服务项目和一个服务安装程序项目。服务项目具有适合我的产品的装配信息。它包括公司信息和正确的服务名称。一旦服务实际安装,所有这些似乎都会被忽略。安装服务时,它使用在服务安装程序的ini
以下代码何时可能产生副作用? @some = map { s/xxx/y/; $_ } @some; perlcritic 将其解释为危险的,因为例如: @other = map { s/xxx/y/
我想知道以下哪种解决方案更好:我想修改一些 .class 文件,我意识到有两种方法可以做到这一点: 反编译.class文件,修改它,最后再次编译。 - 直接用十六进制编辑器修改。 谢谢 最佳答案 在这
这是我的按钮代码 onclick 我希望我的程序等待用户单击一个 JPanel,并且当用户单击 JPanel 时,它应该在控制台上打印其名称。 此按钮代码未显示输出 JPopupMenu popu
我正在使用一个具有“getName()”方法的特定 API。 getName() 返回一个字符串。是否可以修改该字符串? API 中不包含修饰符方法,并且 String getName() 返回的是私
我是一名优秀的程序员,十分优秀!