Code Monkey home page Code Monkey logo

xdl's Introduction

xDL

xDL is an enhanced implementation of the Android DL series functions.

README 中文版

Features

  • Enhanced dlopen() + dlsym() + dladdr().
    • Bypass the restrictions of Android 7.0+ linker namespace.
    • Lookup dynamic link symbols in .dynsym.
    • Lookup debuging symbols in .symtab and ".symtab in .gnu_debugdata".
  • Enhanced dl_iterate_phdr().
    • Compatible with Android 4.x on ARM32.
    • Including linker / linker64 (for Android <= 8.x).
    • Return full pathname instead of basename (for Android 5.x).
    • Return app_process32 / app_process64 instead of package name.
  • Support Android 4.1 - 14 (API level 16 - 34).
  • Support armeabi-v7a, arm64-v8a, x86 and x86_64.
  • MIT licensed.

Artifacts Size

If xDL is compiled into an independent dynamic library:

ABI Compressed (KB) Uncompressed (KB)
armeabi-v7a 7.5 13
arm64-v8a 8.5 18
x86 8.5 17
x86_64 8.7 18

Usage

1. Add dependency in build.gradle

xDL is published on Maven Central, and uses Prefab package format for native dependencies, which is supported by Android Gradle Plugin 4.0+.

android {
    buildFeatures {
        prefab true
    }
}

dependencies {
    implementation 'io.github.hexhacking:xdl:2.1.1'
}

NOTE:

  1. Starting from version 2.0.0 of xDL, group ID changed from io.hexhacking to io.github.hexhacking.
version range group ID artifact ID Repository URL
[1.0.3, 1.2.1] io.hexhacking xdl repo
[2.0.0, ) io.github.hexhacking xdl repo
  1. xDL uses the prefab package schema v2, which is configured by default since Android Gradle Plugin 7.1.0. If you are using Android Gradle Plugin earlier than 7.1.0, please add the following configuration to gradle.properties:
android.prefabVersion=2.0.0

2. Add dependency in CMakeLists.txt or Android.mk

CMakeLists.txt

find_package(xdl REQUIRED CONFIG)

add_library(mylib SHARED mylib.c)
target_link_libraries(mylib xdl::xdl)

Android.mk

include $(CLEAR_VARS)
LOCAL_MODULE           := mylib
LOCAL_SRC_FILES        := mylib.c
LOCAL_SHARED_LIBRARIES += xdl
include $(BUILD_SHARED_LIBRARY)

$(call import-module,prefab/xdl)

3. Specify one or more ABI(s) you need

android {
    defaultConfig {
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }
}

4. Add packaging options

If you are using xDL in an SDK project, you may need to avoid packaging libxdl.so into your AAR, so as not to encounter duplicate libxdl.so file when packaging the app project.

android {
    packagingOptions {
        exclude '**/libxdl.so'
    }
}

On the other hand, if you are using xDL in an APP project, you may need to add some options to deal with conflicts caused by duplicate libxdl.so file.

android {
    packagingOptions {
        pickFirst '**/libxdl.so'
    }
}

There is a sample app in the xdl-sample folder you can refer to.

API

#include "xdl.h"

1. xdl_open() and xdl_close()

#define XDL_DEFAULT           0x00
#define XDL_TRY_FORCE_LOAD    0x01
#define XDL_ALWAYS_FORCE_LOAD 0x02

void *xdl_open(const char *filename, int flags);
void *xdl_close(void *handle);

They are very similar to dlopen() and dlclose(). But xdl_open() can bypass the restrictions of Android 7.0+ linker namespace.

Depending on the value of the flags parameter, the behavior of xdl_open() will have some differences:

  • XDL_DEFAULT: If the library has been loaded into memory, xdl_open() will not dlopen() it again. (But it will still return a valid handle)
  • XDL_TRY_FORCE_LOAD: If the library has not been loaded into memory, xdl_open() will try to dlopen() it.
  • XDL_ALWAYS_FORCE_LOAD: xdl_open() will always dlopen() the library.

If xdl_open() really uses dlopen() to load the library, xdl_close() will return the handle from linker (the return value of dlopen()), and then you can decide whether and when to close it with standard dlclose(). Otherwise, NULL will be returned.

filename can be basename or full pathname. However, Android linker has used the namespace mechanism since 7.0. If you pass basename, you need to make sure that no duplicate ELF is loaded into the current process. xdl_open() will only return the first matching ELF. Please consider this fragment of /proc/self/maps on Android 10:

756fc2c000-756fc7c000 r--p 00000000 fd:03 2985  /system/lib64/vndk-sp-29/libc++.so
756fc7c000-756fcee000 --xp 00050000 fd:03 2985  /system/lib64/vndk-sp-29/libc++.so
756fcee000-756fcef000 rw-p 000c2000 fd:03 2985  /system/lib64/vndk-sp-29/libc++.so
756fcef000-756fcf7000 r--p 000c3000 fd:03 2985  /system/lib64/vndk-sp-29/libc++.so
7571fdd000-757202d000 r--p 00000000 07:38 20    /apex/com.android.conscrypt/lib64/libc++.so
757202d000-757209f000 --xp 00050000 07:38 20    /apex/com.android.conscrypt/lib64/libc++.so
757209f000-75720a0000 rw-p 000c2000 07:38 20    /apex/com.android.conscrypt/lib64/libc++.so
75720a0000-75720a8000 r--p 000c3000 07:38 20    /apex/com.android.conscrypt/lib64/libc++.so
760b9df000-760ba2f000 r--p 00000000 fd:03 2441  /system/lib64/libc++.so
760ba2f000-760baa1000 --xp 00050000 fd:03 2441  /system/lib64/libc++.so
760baa1000-760baa2000 rw-p 000c2000 fd:03 2441  /system/lib64/libc++.so
760baa2000-760baaa000 r--p 000c3000 fd:03 2441  /system/lib64/libc++.so

2. xdl_sym() and xdl_dsym()

void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size);
void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size);

They are very similar to dlsym(). They all takes a "handle" of an ELF returned by xdl_open() and the null-terminated symbol name, returning the address where that symbol is loaded into memory.

If the symbol_size parameter is not NULL, it will be assigned as "the bytes occupied by the content corresponding to the symbol in the ELF". If you don't need this information, just pass NULL.

xdl_sym() lookup "dynamic link symbols" in .dynsym as dlsym() does.

xdl_dsym() lookup "debuging symbols" in .symtab and ".symtab in .gnu_debugdata".

Notice:

  • The symbol sets in .dynsym and .symtab do not contain each other. Some symbols only exist in .dynsym, and some only exist in .symtab. You may need to use tools such as readelf to determine which ELF section the symbol you are looking for is in.
  • xdl_dsym() needs to load debuging symbols from disk file, and xdl_sym() only lookup dynamic link symbols from memory. So xdl_dsym() runs slower than xdl_sym().
  • The dynamic linker only uses symbols in .dynsym. The debugger actually uses the symbols in both .dynsym and .symtab.

3. xdl_addr()

typedef struct
{
    const char       *dli_fname;
    void             *dli_fbase;
    const char       *dli_sname;
    void             *dli_saddr;
    size_t            dli_ssize;
    const ElfW(Phdr) *dlpi_phdr;
    size_t            dlpi_phnum;
} xdl_info_t;

int xdl_addr(void *addr, xdl_info_t *info, void **cache);
void xdl_addr_clean(void **cache);

xdl_addr() is similar to dladdr(). But there are a few differences:

  • xdl_addr() can lookup not only dynamic link symbols, but also debugging symbols.
  • xdl_addr() uses the xdl_info_t structure instead of the Dl_info structure, which contains more extended information: dli_ssize is the number of bytes occupied by the current symbol; dlpi_phdr points to the program headers array of the ELF where the current symbol is located; dlpi_phnum is the number of elements in the dlpi_phdr array.
  • xdl_addr() needs to pass an additional parameter (cache), which will cache the ELF handle opened during the execution of xdl_addr(). The purpose of caching is to make subsequent executions of xdl_addr() of the same ELF faster. When you do not need to execute xdl_addr(), please use xdl_addr_clean() to clear the cache. For example:
void *cache = NULL;
xdl_info_t info;
xdl_addr(addr_1, &info, &cache);
xdl_addr(addr_2, &info, &cache);
xdl_addr(addr_3, &info, &cache);
xdl_addr_clean(&cache);

4. xdl_iterate_phdr()

#define XDL_DEFAULT       0x00
#define XDL_FULL_PATHNAME 0x01

int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags);

xdl_iterate_phdr() is similar to dl_iterate_phdr(). But xdl_iterate_phdr() is compatible with android 4.x on ARM32, and always including linker / linker64.

xdl_iterate_phdr() has an additional "flags" parameter, one or more flags can be bitwise-or'd in it:

  • XDL_DEFAULT: Default behavior.
  • XDL_FULL_PATHNAME: Always return full pathname instead of basename.

These flags are needed because these capabilities require additional execution time, and you don't always need them.

5. xdl_info()

#define XDL_DI_DLINFO 1  // type of info: xdl_info_t

int xdl_info(void *handle, int request, void *info);

xdl_info() is similar to dlinfo(). xdl_info() obtains information about the dynamically loaded object referred to by handle (obtained by an earlier call to xdl_open).

The only request parameter currently supported is XDL_DI_DLINFO, which means to return data of type xdl_info_t through the info parameter (note that the values of dli_sname, dli_saddr, dli_ssize in the returned xdl_info_t at this time both are 0).

On success, xdl_info() returns 0. On failure, it returns -1.

Support

Contributing

License

xDL is MIT licensed, as found in the LICENSE file.

History

xCrash 2.x contains a very rudimentary module xc_dl for searching system library symbols, which has many problems in performance and compatibility. xCrash 2.x uses it to search a few symbols from libart, libc and libc++.

Later, some other projects began to use the xc_dl module alone, including in some performance-sensitive usage scenarios. At this time, we began to realize that we need to rewrite this module, and we need a better implementation.

xdl's People

Contributors

caikelun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xdl's Issues

请问 xdl_sym 与 xdl_dsym 的实现是不是反了

xDL Version

1.1.4

Android OS Version

不限版本

Android ABIs

armeabi-v7a, arm64-v8a, x86, x86_64

Device Manufacturers and Models

不限设备

Describe the Bug

xdl_sym 内部实现是读取 .dynsym,xdl_dsym 内部实现是读取 .symtab,会不会是搞反了,还是有什么特殊含义?

Static library

the Feature, Motivation and Pitch

If it is possible, could a static version of the library be shipped in addition to the shared version? I am not sure about the limitations of Prefab, but I hope that it can be done. This would be very helpful for building it into my own native library without the need for 2+ shared libraries (and can potentially cause conflicts, etc.).

Alternatives

Currently I manually download and modify the repo to build a static version to implement into my own native library.

Additional context

No response

info->dlpi_name指向了一个野指针

xDL Version

1.1.3

Android OS Version

9.0

Android ABIs

armeabi-v7a

Device Manufacturers and Models

Google Pixel 3

Describe the Bug

info->dlpi_name = (const char *)buf;

老师你好,
buf在作用域结束时变成了野指针,callback继续操作info->dlpi_name的话非常危险。
解决方案:在堆上malloc一段空间存放pathname;在callback结束后进行free

xdl_open crash

版本:

implementation 'io.hexhacking:xdl:1.0.4'

执行条件:

在APP主进程的主线程中通过jni调用xdl_open,加载libbluetooth.so (/system/lib64/libbluetooth.so)
调用xdl_open之前,该so没有加载到进程的内存空间中,是第一次加载

崩溃日志:

`*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/flame/flame:11/RP1A.200720.009/6720564:user/release-keys'
Revision: 'MP1.0'
ABI: 'arm64'
Timestamp: 2021-04-25 17:29:56+0800
pid: 15387, tid: 15387, name: example.bledemo >>> com.example.bledemo <<<
uid: 10030
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Cause: null pointer dereference
x0 0000000000000006 x1 0000007fc98f3188 x2 0000007fc98f31c0 x3 0000007199bf9080
x4 000000719cafd740 x5 000000719cafd733 x6 2f6d65747379732f x7 696c2f343662696c
x8 000000719dcdd4a8 x9 0000000000000000 x10 000000000000003f x11 0000000000000028
x12 65756c6262696c2f x13 6f732e68746f6f74 x14 00000000afd231bc x15 0000000002bf48c6
x16 000000719dcd9448 x17 000000719dcae4b0 x18 0000000000000000 x19 0000007199bf9080
x20 000000719dbf3058 x21 0000006ef6095390 x22 0000000000000000 x23 000000719dbf6f61
x24 0000000000000066 x25 000000719dcdd4b8 x26 000000719dcdd4b0 x27 0000006ef5b7b86c
x28 000000719dcdd000 x29 0000007fc98eb8b0
lr 000000719dc380f4 sp 0000007fc98eb8b0 pc 0000006ef5b7b86c pst 0000000080000000

backtrace:
#00 pc 000000000017b86c /system/lib64/libbluetooth.so (_GLOBAL__sub_I_bta_ag_act.cc) (BuildId: d07643cbd5d9d34c52b587b199da32f3)
#1 pc 000000000004a0f0 /apex/com.android.runtime/bin/linker64 (_dl__ZL10call_arrayIPFviPPcS1_EEvPKcPT_mbS5+284) (BuildId: 3616c064c2d540887bd8b30030a981de)
#2 pc 000000000004a2f0 /apex/com.android.runtime/bin/linker64 (__dl__ZN6soinfo17call_constructorsEv+380) (BuildId: 3616c064c2d540887bd8b30030a981de)
#3 pc 0000000000035a4c /apex/com.android.runtime/bin/linker64 (__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv+2088) (BuildId: 3616c064c2d540887bd8b30030a981de)
#4 pc 00000000000310e8 /apex/com.android.runtime/bin/linker64 (__dl__ZL10dlopen_extPKciPK17android_dlextinfoPKv+80) (BuildId: 3616c064c2d540887bd8b30030a981de)
#5 pc 000000000000120c /data/app/~~yxqS3Fy6-6fNtXX7A_xKMw==/com.example.bledemo-QfW-KfzEF2wbfbY9Arjt7Q==/base.apk!libxdl.so (offset 0xe000) (xdl_open+128) (BuildId: 154fa0245579a20c11a832dd43fe69784a9c157a)
#6 pc 0000000000000980 /data/app/~~yxqS3Fy6-6fNtXX7A_xKMw==/com.example.bledemo-QfW-KfzEF2wbfbY9Arjt7Q==/base.apk!libble_compat.so (offset 0x7000) (BuildId: 2f66c2f22a2ba64935b061936938d659925aaf79)
#7 pc 00000000000008b8 /data/app/~~yxqS3Fy6-6fNtXX7A_xKMw==/com.example.bledemo-QfW-KfzEF2wbfbY9Arjt7Q==/base.apk!libble_compat.so (offset 0x7000) (Java_com_connect_ble_BLECompat_getBLEAddress+32) (BuildId: 2f66c2f22a2ba64935b061936938d659925aaf79)`

tombstone_pixel_4_android_11.txt
tombstone_pixel_xl_android_10.txt
tombstone_vivo_y15_android_11.txt

Impossible to use vendor namespace libraries

xDL Version

v1.1.3

Android OS Version

11-12

Android ABIs

armeabi-v7a, arm64-v8a

Device Manufacturers and Models

Xiaomi mi9, Xiaomi mi10 ultra, Motorola moto g8, etc.

Describe the Bug

Every time I trying to xdl_open library from system/vendor/lib64 there's no success.

Can't use vendor namespace libraries

xDL Version

2.1.1

Android OS Version

14

Android ABIs

armeabi-v7a, arm64-v8a

Device Manufacturers and Models

Samsung Galaxy A53 5G

Describe the Bug

xdl_open returns null if I try to open any library in the vendor namespace. Attaching log from sample app.

xdl_tag                 com.mdnssknght.mycamera              I  +++ xdl_open + xdl_info + xdl_dsym + xdl_addr
xdl_tag                 com.mdnssknght.mycamera              I  >>> xdl_open(libart.so) : handle b4000071f32906d0
xdl_tag                 com.mdnssknght.mycamera              I  >>> xdl_info(b4000071f32906d0) : 71afc12000 /apex/com.android.art/lib64/libart.so (phdr 71afc12040, phnum 10)
xdl_tag                 com.mdnssknght.mycamera              I  >>> xdl_dsym(_ZN3artL16FindOatMethodForEPNS_9ArtMethodENS_11PointerSizeEPb) : addr 71afefb4cc, sz 3400
xdl_tag                 com.mdnssknght.mycamera              I  >>> xdl_addr(71afefb4cc) : 71afc12000 /apex/com.android.art/lib64/libart.so (phdr 71afc12040, phnum 10), 71afefb4cc _ZN3artL16FindOatMethodForEPNS_9ArtMethodENS_11PointerSizeEPb.__uniq.231987612005477677052516648077052451092.llvm.3250098303042339366 (sz 3400)
...
xdl_tag                 com.mdnssknght.mycamera              I  --- dlopen(libOpenCL.so) : handle 0
xdl_tag                 com.mdnssknght.mycamera              I  +++ xdl_open + xdl_info + xdl_sym + xdl_addr
xdl_tag                 com.mdnssknght.mycamera              I  >>> xdl_open(libOpenCL.so) : handle 0
xdl_tag                 com.mdnssknght.mycamera              I  >>> xdl_info(0) : 0 (NULL) (phdr 0, phnum 0)
xdl_tag                 com.mdnssknght.mycamera              I  >>> xdl_sym(clCreateContext) : addr 0, sz 0
xdl_tag                 com.mdnssknght.mycamera              I  >>> xdl_addr(0) : FAILED

对于未加载的so,无法正常打开

xDL Version

2.0.0

Android OS Version

8.1

Android ABIs

armeabi-v7a, arm64-v8a

Device Manufacturers and Models

Nexus 6p

Describe the Bug

执行函数LOGE("xdl_open1 %p",xdl_open("libxxx.so",XDL_TRY_FORCE_LOAD));
我在函数进行了插桩输出
LOGE("xdl_linker_dlopen %p",xdl_linker_dlopen);
LOGE("xdl_linker_load// >= Android 8.0");
LOGE("xdl_linker_caller_addr[%d] %p",i,xdl_linker_caller_addr[i]);
LOGE("xdl_linker_load// >= Android 8.0 handle:%p",handle);
以下是结果
2023-12-25 09:45:52.485 21743-22152 MZHY com.mz.fkrdcg.a233 E xdl_linker_dlopen 0x78514dde3c
2023-12-25 09:45:52.485 21743-22152 MZHY com.mz.fkrdcg.a233 E xdl_linker_load// >= Android 8.0
2023-12-25 09:45:52.485 21743-22152 MZHY com.mz.fkrdcg.a233 E xdl_linker_caller_addr[0] 0x7850649000
2023-12-25 09:45:52.501 21743-22152 MZHY com.mz.fkrdcg.a233 E xdl_linker_caller_addr[1] 0x77cc39e000
2023-12-25 09:45:52.512 21743-22152 MZHY com.mz.fkrdcg.a233 E xdl_linker_caller_addr[2] 0x77c2047000
2023-12-25 09:45:52.513 21743-22152 MZHY com.mz.fkrdcg.a233 E xdl_linker_load// >= Android 8.0 handle:0x0
2023-12-25 09:45:52.513 21743-22152 MZHY com.mz.fkrdcg.a233 E xdl_open1 0x0
可以看到xdl_open返回的是null.

值得一提的是我直接使用dlopen反而还能正常打开so获取handle,先用dlopen打开so,再执行xdl_open也能获取到handle

vivo Y35 android5.0有些符号找不到

xDL Version

2.0.0

Android OS Version

5.0

Android ABIs

arm64-v8a

Device Manufacturers and Models

vivo Y35

Describe the Bug

本地使用nm -D可以找到符号, 但是xDL找不到符号对应的函数

*** --------------------------------------------------------------
2023-01-09 10:56:34.465 23494-23494 xdl_tag io.github.hexhacking.xdl.sample I +++ xdl_open + xdl_info + xdl_dsym + xdl_addr
2023-01-09 10:56:34.466 23494-23494 xdl_tag io.github.hexhacking.xdl.sample I >>> xdl_open(libart.so) : handle 558ed70050
2023-01-09 10:56:34.466 23494-23494 xdl_tag io.github.hexhacking.xdl.sample I >>> xdl_info(558ed70050) : 7f9e56e000 libart.so (phdr 7f9e56e040, phnum 6)
2023-01-09 10:56:34.476 23494-23494 xdl_tag io.github.hexhacking.xdl.sample I >>> xdl_dsym(ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPS9) : addr 0, sz 0
2023-01-09 10:56:34.476 23494-23494 xdl_tag io.github.hexhacking.xdl.sample I >>> xdl_addr(0) : FAILED
2023-01-09 10:56:34.476 23494-23494 xdl_tag io.github.hexhacking.xdl.sample I *** --------------------------------------------------------------

Android9.0-Android6.0 arm64模式下获取libart.so加载基地址是有问题的

xDL Version

1.1.3

Android OS Version

8.1.0

Android ABIs

arm64-v8a

Device Manufacturers and Models

小米平板4

Describe the Bug

2023-03-02_140458

2023-03-02_135959

2023-03-02_140009

void *so_base_addr = xdlInfo.dli_fbase;
so_base_addr 输出就是soStartAddrFromMaps这项的地址为0x799cbdb000,但是我自己手动扫描maps时,发现libart.so的加载地址,也就是第一项,为0x799cc00000。目前只发现在 Android9.0-Android6.0 arm64/32模式下libart.so会出现此情况。libc.so都是正确的,没有任何问题。(自身做了mmap操作,但是都在调用xdl之前munmap了或者在xdl调用之后mmap)

在雷电模拟器中获取动态库libart.so 地址失败

xDL Version

2.0.0

Android OS Version

9.0

Android ABIs

armeabi-v7a

Device Manufacturers and Models

雷电模拟器 9.0.35 android 9.0(64位)

Describe the Bug

我打了一个包含32位和64位动态库的apk,安装到雷电模拟器后,尝试
xdl_open(“libart.so”, XDL_DEFAULT);
结果返回了nullptr
需要特别说明的是,雷电模拟器运行的是32的动态库(实际我提供了64位的库,并且模拟器自身也是64位的)
image

image

同样的代码,我在真机上尝试过,是可以正常获取到目标地址的

能否为xdl_open添加一些flag参数?

我使用xdl加载一个so库(此前该so从未被加载到内存),但它加载失败了,它似乎是由undefined symbol: xxxx导致的,某些符号未被定义。仅当我使用dlopen并使用RTLD_LAZY才会成功。而我注意到XDL默认使用RTLD_NOW来加载库,能否为xdl_open添加一些flag参数,与dlopen相同?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.