作为Linux上的新手,我有一些问题:
如何从程序加载C运行时?
路径和文件名是什么?
在Windows上
C:\Windows\System32\msvcrt.dll
顺便问一下,Linux上调用的DLL是什么?
与MS LoadLibrary
和GetProcAddress
对应的功能有哪些?
它们驻留在什么DLL中?
我必须链接哪个图书馆才能访问它们?
在Windows下,它是kernel32.lib
和kernel32.dll
。
最后:如何编写独立于目标平台的Masm / JWasm代码?
是否有反映命令行上使用的目标arg的汇编符号?
编辑:我忘了问几个问题:Linux是64位还是32位?它是否使用与64位MS C相同的调用约定?我的意思是rcx,rdx,r8,r9等参数。
答案 0 :(得分:6)
典型的类UNIX系统将ELF文件格式用于目标文件,共享库和二进制文件。许多东西与Windows类似,但有些是不同的。首先,这是一个常用后缀列表:
UNIX Windows
*.o *.obj object file
*.a *.lib static library
*.so *.dll shared object (ELF targets)
*.dylib *.dll shared object (Mach O targets, i.e. Mac OS X)
* *.exe binary (no suffix on UNIX)
静态链接的工作方式与Windows上的相同,但动态链接不同。首先,共享库没有lib文件。相反,静态链接器ld
为您希望直接调用共享对象时调用的每个函数生成一个适当的PLT(过程链接表)和存根。在此使用方案中,您链接到的共享对象存储在二进制文件的特殊部分中。您的二进制文件不是直接执行的,而是将动态加载程序ld.so
作为二进制文件的解释器加载,查找所有需要的共享对象并将它们加载到地址空间中。符号引用在第一次调用时解析(除非您另行指定)。
您还可以使用dl
库在运行时加载共享对象,该库提供函数dlopen
,dlclose
,dlsym
和dlerror
以加载共享对象和访问符号。请注意,dl
库可能取决于可用的libc部分,因为所有系统调用都是通过libc完成的。在某些类UNIX操作系统上,libdl已集成到libc中。
您当然也可以手动加载共享对象,但这样做很复杂。
没有直接相当于Microsoft的kernel32.dll
。系统调用在C标准库libc.so
中实现(也可以静态地用作libc.a
),但您也可以直接调用操作系统。装配系统调用接口的稳定性因操作系统而异,但libc接口是稳定的。我强烈建议您使用libc专门调用操作系统。
CRT包含libc和几个目标文件,当您通过C编译器链接时,这些目标文件链接到您的程序中。这些目标文件从堆栈中检索参数向量和ELF辅助向量,并调用libc。然后libc为您调用main
。我强烈建议您始终链接C编译器,并使汇编程序从main
开始。这样可以实现更轻松,更便携的编程。
如需进一步阅读,我建议您阅读SysV ABI和ELF格式的文档。
如果你想使用libc,我建议你链接C编译器:
cc -o binary object1.o object2.o ... -llibrary1 -llibrary2 ...
这也将CRT条目存根链接到您的程序中;程序本身应该提供一个名为main
的全局符号,它由ABI调用约定中的libc调用(在i386上,cdecl用于所有函数,在amd64上使用SysV ABI调用约定) 。签名如下:
main(argc, argv, envp);
其中argc
是命令行参数的数量,argv是指向命令行参数的以null结尾的指针数组的指针,而envp是指向以null结尾的环境变量数组的指针(每个entry的格式为key=value
。如果main
返回,则C运行时调用exit
函数,返回值为main
作为退出状态。注意{{1}刷新所有stdio缓冲区,这样你就不必这样做了。
您还可以使用exit
选项创建静态二进制文件:
-static
请注意,某些libc功能(例如DNS查找)需要动态加载共享库,如果您使用这些功能中的任何一个,则在执行程序期间可能会打开共享库。
如果您不想使用libc,请将程序与cc -static -o binary object1.o object2.o ... -llibrary1 -llibrary2 ...
链接器链接:
ld
在这种情况下,程序的入口点是一个名为ld -o binary object1.o ... -llibrary1 ...
的全局符号,操作系统在启动时会在堆栈上放置一堆有用的东西,请阅读上述ABI文档以获取详细信息。
_start
,stdin
和stdout
分别可用作文件描述符0,1和2。不保证它们是开放的。您可以使用标准POSIX函数stderr
和read()
来读取和写入数据。访问指向C write()
结构FILE
,stdin
和stdout
的指针取决于您为其编程的操作系统。在Linux上,这些只是外部符号,但在其他系统(例如FreeBSD)上,您可能需要调用函数来获取指针。
答案 1 :(得分:1)
如何从程序加载C运行时?
通常在调用main
之前很久就会将C运行时加载到程序映像中。实际上它是正在进行main
调用的运行时。顺便说一下,它也就像在Windows中一样;具有轻微的特性,Windows程序可以WinMain
而不是main
。
但是,如果您打算在C运行时内找到符号地址,那么您可以dlopen
代替GetModuleHandle
或LoadLibrary
。
在Windows上
C:\Windows\System32\msvcrt.dll
实际上它是%SYSPATH%\msvcrt.dll
,但是在旁边挑剔......
在* nix-systems上(通常)/lib/libc.so
顺便问一下,Linux上调用的DLL是什么?
共享对象。因此文件后缀为.so
- 或者更确切地说是.so.$VERSION
,例如libsomething.so.1.2
关于共享对象的强制性阅读:https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf
MS LoadLibrary和GetProcAddress对应的功能是什么?
dlopen
和dlsym
它们驻留在什么DLL中?
它们位于libdl.so
我必须链接哪个库才能访问它们?
libdl.so
答案 2 :(得分:1)
我找到了这些不错的解决方案,没有我自己的发明:
;--- "hello world" for Linux which uses int 80h.
;--- assemble: jwasm -Fo=Linux1.o Linux1.asm
;--- link: jwlink format ELF runtime linux file Linux1.o name Linux1
.386
.model flat
stdout equ 1
SYS_EXIT equ 1
SYS_WRITE equ 4
.data
string db 10,"Hello, world!",10
.code
_start:
mov ecx, offset string
mov edx, sizeof string
mov ebx, stdout
mov eax, SYS_WRITE
int 80h
mov eax, SYS_EXIT
int 80h
end _start
啊,那太好了。这一个:
;--- "hello world" for 64-bit Linux, using SYSCALL.
;--- assemble: JWasm -elf64 -Fo=Lin64_1.o Lin64_1.asm
;--- link: gcc Lin64_1.o -o Lin64_1
stdout equ 1
SYS_WRITE equ 1
SYS_EXIT equ 60
.data
string db 10,"Hello, world!",10
.code
_start:
mov edx, sizeof string
mov rsi, offset string
mov edi, stdout
mov eax, SYS_WRITE
syscall
mov eax, SYS_EXIT
syscall
end _start
辉煌!这就是我在寻找的东西。低级别的东西。第一个就像旧的MSDOS和Linux系统的int 80h接口一样。另一个使用syscall指令。
这很简单!在Windows中没有任何类似的东西。似乎asm progr在Linux下比赢得更容易。然而,这提出了一些问题:
syscall instr仅在64位Linux下可用吗?
通过syscall / int 80h可以获得系统例程吗?它们丰富吗?你能没有clib.so吗?
上述其他人的贡献虽然很有价值,但显然是C程序员的工作。我更像是在asm工作的Linux bit黑客。
特别是,是否有系统调用加载SO并查询功能地址?
答案 3 :(得分:0)
分别为MS LoadLibrary和GetProcAddress对应的功能是什么?
dlopen
和dlsym
。
它们驻留在什么DLL中?
通常libdl
。在链接器命令行中使用-ldl
来附加它。